From 1f04910cc57ee69080edc5027685ba3b2f353f1a Mon Sep 17 00:00:00 2001 From: Peter Osterlund Date: Sat, 12 Nov 2011 19:44:09 +0000 Subject: [PATCH] Moved DroidFishTest project to trunk/ --- DroidFishTest/.classpath | 9 + DroidFishTest/.project | 34 + DroidFishTest/AndroidManifest.xml | 19 + .../bin/res/drawable-hdpi/ic_launcher.png | Bin 0 -> 3966 bytes .../bin/res/drawable-ldpi/ic_launcher.png | Bin 0 -> 1537 bytes .../bin/res/drawable-mdpi/ic_launcher.png | Bin 0 -> 2200 bytes DroidFishTest/proguard.cfg | 40 ++ DroidFishTest/project.properties | 11 + .../res/drawable-hdpi/ic_launcher.png | Bin 0 -> 4147 bytes .../res/drawable-ldpi/ic_launcher.png | Bin 0 -> 1723 bytes .../res/drawable-mdpi/ic_launcher.png | Bin 0 -> 2574 bytes DroidFishTest/res/layout/main.xml | 12 + DroidFishTest/res/values/strings.xml | 7 + .../org/petero/droidfish/engine/BookTest.java | 78 +++ .../droidfish/engine/PolyglotBookTest.java | 82 +++ .../petero/droidfish/gamelogic/GameTest.java | 461 +++++++++++++ .../droidfish/gamelogic/GameTreeTest.java | 651 ++++++++++++++++++ .../droidfish/gamelogic/MoveGenTest.java | 204 ++++++ .../petero/droidfish/gamelogic/MoveTest.java | 59 ++ .../petero/droidfish/gamelogic/PieceTest.java | 42 ++ .../droidfish/gamelogic/PositionTest.java | 455 ++++++++++++ .../droidfish/gamelogic/TextIOTest.java | 382 ++++++++++ .../droidfish/gamelogic/TimeControlTest.java | 136 ++++ 23 files changed, 2682 insertions(+) create mode 100644 DroidFishTest/.classpath create mode 100644 DroidFishTest/.project create mode 100644 DroidFishTest/AndroidManifest.xml create mode 100644 DroidFishTest/bin/res/drawable-hdpi/ic_launcher.png create mode 100644 DroidFishTest/bin/res/drawable-ldpi/ic_launcher.png create mode 100644 DroidFishTest/bin/res/drawable-mdpi/ic_launcher.png create mode 100644 DroidFishTest/proguard.cfg create mode 100644 DroidFishTest/project.properties create mode 100644 DroidFishTest/res/drawable-hdpi/ic_launcher.png create mode 100644 DroidFishTest/res/drawable-ldpi/ic_launcher.png create mode 100644 DroidFishTest/res/drawable-mdpi/ic_launcher.png create mode 100644 DroidFishTest/res/layout/main.xml create mode 100644 DroidFishTest/res/values/strings.xml create mode 100644 DroidFishTest/src/org/petero/droidfish/engine/BookTest.java create mode 100644 DroidFishTest/src/org/petero/droidfish/engine/PolyglotBookTest.java create mode 100644 DroidFishTest/src/org/petero/droidfish/gamelogic/GameTest.java create mode 100644 DroidFishTest/src/org/petero/droidfish/gamelogic/GameTreeTest.java create mode 100644 DroidFishTest/src/org/petero/droidfish/gamelogic/MoveGenTest.java create mode 100644 DroidFishTest/src/org/petero/droidfish/gamelogic/MoveTest.java create mode 100644 DroidFishTest/src/org/petero/droidfish/gamelogic/PieceTest.java create mode 100644 DroidFishTest/src/org/petero/droidfish/gamelogic/PositionTest.java create mode 100644 DroidFishTest/src/org/petero/droidfish/gamelogic/TextIOTest.java create mode 100644 DroidFishTest/src/org/petero/droidfish/gamelogic/TimeControlTest.java diff --git a/DroidFishTest/.classpath b/DroidFishTest/.classpath new file mode 100644 index 0000000..2604b8a --- /dev/null +++ b/DroidFishTest/.classpath @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/DroidFishTest/.project b/DroidFishTest/.project new file mode 100644 index 0000000..57d4422 --- /dev/null +++ b/DroidFishTest/.project @@ -0,0 +1,34 @@ + + + DroidFishTest + + + DroidFish + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/DroidFishTest/AndroidManifest.xml b/DroidFishTest/AndroidManifest.xml new file mode 100644 index 0000000..b87b05e --- /dev/null +++ b/DroidFishTest/AndroidManifest.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/DroidFishTest/bin/res/drawable-hdpi/ic_launcher.png b/DroidFishTest/bin/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..882eb148ae9ed4d0283f226739560644923e1fee GIT binary patch literal 3966 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 literal 0 HcmV?d00001 diff --git a/DroidFishTest/bin/res/drawable-mdpi/ic_launcher.png b/DroidFishTest/bin/res/drawable-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..02e96b96078eaf477369b48eea156f30d9651877 GIT binary patch literal 2200 zcmV;J2xs?+P)$TU7Oh2c+`S zhPG-8YWqM@RVZB|s#2<|c?pn&0wrleAb}7klf}ES$1|Sw`|dq`n9q*m*y9<;*e|`K zqmgE=?mg@OoO=%Ze=Y1%bG&b^m&}$i1!%wpY65a5{QhAs8!&EwVnF_c-_Kj_G>2#) z1jKH-t6}36TjEbz<<}s3ek`G^XmN{2-yD5#@6P05AOm=TP!(Aq48*?w%a1M*QZ(z5E5e@3}6*l2G$A5*ZL_!($)&r3Sil~@*$Q) zFJ&at!$9T;S9g4kmiT&JJ@s=&^F4fK-4jGZi#a*Chs&G3z$Nw9aiHsIHn!bOyk;pc z9Qg*uHx&IM9r3NKZ~Y?8(e+qQKwoGBt-&uU9k{ffj?ro1)dp@OB3NT-k8k1CQ%|zG z=~g^bWK-LA&J4fK>c(4XsoTt8=6y27L3~?aMZ=8@XA@YlMBGNYQu}FNbRE%}R^o0O z*DwDzT_gK(l#5`;@0mn@^F;rx2?tniQ)WZsD?~PZ4+9=}*7Q;qK@0 ztcO^|$q)82knSX3$ddQ6{QKmuNoNNbNFV3)(CcWap->#b^Ye%(ItO0H^9$p5Or~r; zMWL9U$Wz5G1i+@YZLC?*hBY4XNIR)q4`)W+VD*xlxoP!xsSCGr?Xvr5TXZG0PCXl! z-i5|R0Zp;BY+e3!JX_$(j_qt}znizZo?~%z4Wq?=l!EP7KSxMMxPHZhK~PV2Aw_4l z7uZo%CRRW@uyp_a|30&M^Tu_?m<54R$8k7x=v-(1>@;sDv zArZmaQh+T8>lNJX&R7lxXs{rqXa)C{6~GvSwH9j^&Xs10KXkksd;NHd4FV1<3Lwxfw>^hOu>SLQ1KU zODG408b4De71Cq-Mn^~K?(U|ir^gKxt*REl^Sp5+x~Pn*Be;Ypm{B}G$8i1>VZDf0 zs2Kr#o10KRXu7+*>Fetw9*+a~zVFYj7P|PBhX$oMJ+hBe$=!6N-yrJ^aQ5hHXceX- zzJ*nbZ)R1)Ev4Y5{#PYG_kMM~VkAf%!jc2m#J+p&kE`dT53WX?o9{ri4 zj1IL7Ax~>`Gw%((NLT7Vgq=E$eegFN9fb=(K#@qkj8+;cNrb{di{?*< zw{hqCXSrd3T>wZ!mlKyY+zNde;F z=m;Obqob zfJ#lN&E%ULj;`glRgWWB_9h-9Ym#i+u#>j%wdkfpGb6IYr86pBotj}t?? zF;Zkh^LAQd8!)zj2$RTSFlDJPRJvFrH5fQTzw<%%7^s>OzGHAygiWnqVa?(@*mwGI z63IQ|?j;jGt`p&%ft_q>yBBr7;-zX7s|G^(`6POaCXO_M7mxoy=Be!)E-ZXOuZU@X*z6$ul;PY7YIJOtLmq{25l_l$V!+Wg zAd0OW-{JYAUt)jHQ#d$;RCLzmxRRhje6erhnQMKA-t-~Pre0%N{q?lOHjvDpD3uZm z`6eEJbd=GcMsuM1HyrN&Bd)IH$ly*sf7w&yy=GB)54vs2&S&#(=f}D}e?e63nXRV|fA7Ngx5d2@C!a4Dn28Jk&Hd6ufdY%NZfe>S()%;}5&tw4|FzinFR`V=?P?`yw3Evlh a>i+;pnk3LzJsKVW0000; +} + +-keepclasseswithmembers class * { + public (android.content.Context, android.util.AttributeSet); +} + +-keepclasseswithmembers class * { + public (android.content.Context, android.util.AttributeSet, int); +} + +-keepclassmembers class * extends android.app.Activity { + public void *(android.view.View); +} + +-keepclassmembers enum * { + public static **[] values(); + public static ** valueOf(java.lang.String); +} + +-keep class * implements android.os.Parcelable { + public static final android.os.Parcelable$Creator *; +} diff --git a/DroidFishTest/project.properties b/DroidFishTest/project.properties new file mode 100644 index 0000000..f049142 --- /dev/null +++ b/DroidFishTest/project.properties @@ -0,0 +1,11 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "ant.properties", and override values to adapt the script to your +# project structure. + +# Project target. +target=android-10 diff --git a/DroidFishTest/res/drawable-hdpi/ic_launcher.png b/DroidFishTest/res/drawable-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..8074c4c571b8cd19e27f4ee5545df367420686d7 GIT binary patch literal 4147 zcmV-35X|q1P)OwvMs$Q8_8nISM!^>PxsujeDCl4&hPxrxkp%Qc^^|l zp6LqAcf3zf1H4aA1Gv-O6ha)ktct9Y+VA@N^9i;p0H%6v>ZJZYQ`zEa396z-gi{r_ zDz)D=vgRv62GCVeRjK{15j7V@v6|2nafFX6W7z2j1_T0a zLyT3pGTubf1lB5)32>bl0*BflrA!$|_(WD2)iJIfV}37=ZKAC zSe3boYtQ=;o0i>)RtBvsI#iT{0!oF1VFeW`jDjF2Q4aE?{pGCAd>o8Kg#neIh*AMY zLl{;F!vLiem7s*x0<9FKAd6LoPz3~G32P+F+cuGOJ5gcC@pU_?C2fmix7g2)SUaQO$NS07~H)#fn!Q<}KQWtX}wW`g2>cMld+`7Rxgq zChaey66SG560JhO66zA!;sK1cWa2AG$9k~VQY??6bOmJsw9@3uL*z;WWa7(Nm{^TA zilc?y#N9O3LcTo2c)6d}SQl-v-pE4^#wb=s(RxaE28f3FQW(yp$ulG9{KcQ7r>7mQ zE!HYxUYex~*7IinL+l*>HR*UaD;HkQhkL(5I@UwN%Wz504M^d!ylo>ANvKPF_TvA< zkugG5;F6x}$s~J8cnev->_(Ic7%lGQgUi3n#XVo36lUpcS9s z)ympRr7}@|6WF)Ae;D{owN1;aZSR50al9h~?-WhbtKK%bDd zhML131oi1Bu1&Qb$Cp199LJ#;j5d|FhW8_i4KO1OI>}J^p2DfreMSVGY9aFlr&90t zyI2FvxQiKMFviSQeP$Ixh#70qj5O%I+O_I2t2XHWqmh2!1~tHpN3kA4n=1iHj?`@c<~3q^X6_Q$AqTDjBU`|!y<&lkqL|m5tG(b z8a!z&j^m(|;?SW(l*?tZ*{m2H9d&3jqBtXh>O-5e4Qp-W*a5=2NL&Oi62BUM)>zE3 zbSHb>aU3d@3cGggA`C-PsT9^)oy}%dHCaO~nwOrm5E54=aDg(&HR4S23Oa#-a^=}w%g?ZP-1iq8PSjE8jYaGZu z$I)?YN8he?F9>)2d$G6a*zm0XB*Rf&gZAjq(8l@CUDSY1tB#!i> zW$VfG%#SYSiZ};)>pHA`qlfDTEYQEwN6>NNEp+uxuqx({Fgr zjI@!4xRc?vk^9+~eU|mzH__dCDI=xb{Cd}4bELS9xRaS!*FXMwtMR-RR%SLMh0Cjl zencr8#Su<4(%}$yGVBU-HX{18v=yPH*+%^Vtknc>2A;%-~DrYFx^3XfuVgvZ{#1tA== zm3>IzAM2{3Iv_d1XG{P6^tN3|PkJMnjs&CWN7%7_CmjoVakUhsa&dMv==2~^ri?&x zVdv*rnfVyM+I1^Kg*S=23mR@+0T9BWFZUu~@toA8d)fw6be=`Yb6DSX6D?jB%2YT~ z*aHjtIOozfMhA!Jd*?u5_n!SnX>vX`=Ti-1HA4RiE>eI3vTn zz+>Ccf0HX6Ans-ebOB>RJST-Cyr#4XAk+mAlJgdQnoE{^iIN)OcYFSpgJUmXtl@tT z-^ZuUeSj5hSFrQwqX>~EtZ*{>Gi8Bu9_|o06oNtaXP?E936!a@DsvS*tsB@fa6kEA z5GkjwmH?EgpiG&itsB_Tb1NxtFnvxh_s@9KYX1Sttf?AlI~)z zT=6Y7ulx=}<8Scr_UqU-_z)5gPo%050PsbM*ZLno;_-ow&k?FZJtYmb2hPA$LkP)8 z=^d0Q6PImh6Y|QT?{grxj)S=uBKvY2EQUbm@ns9^yKiP~$DcD)c$5Em`zDSScH%iH zVov&m=cMo`1tYwA=!a}vb_ef_{)Q2?FUqn>BR$6phXQRv^1%=YfyE-F$AR4Q?9D!f zCzB^^#td~4u&l~l#rp2QLfe3+_ub9@+|x+m;=2(sQ`s%gO|j$XBb>A7Q(UydipiMw%igcweV#Cr~SP);q>w`bxts_4} znKHg?X==JDkQl3Y>Ckt%`s{n?Nq-1Fw5~%Mq$CAsi-`yu_bKm zxs#QdE7&vgJD%M84f4SNzSDv)S|V?|$!d5a#lhT5>>YWE4NGqa9-fbmV$=)@k&32kdEYetna>=j@0>V8+wRsL;po!3ivVwh<9tn z2S<1u9DAAQ>x1Sn=fk`)At|quvleV($B|#Kap_lB-F^*yV=wZ{9baUu(uXfokr95^ zA*!*W=5a>$2Ps`-F^+qRQT^{*cN>vipT*4!r#p%{(#I7s z0NN94*q?ib$KJjfDI_sjHNdmEVp5wB&j54O#VoFqBwy)gfA$%)4d_X4q${L9Xom2R3xy&ZBSNgt4a1d7K^CDWa9r zVb-_52m}Vp)`9;ZSKd#|U4ZYj5}Gp49{4utST|=c`~(#>KHF6}CCov1iHYw zt{bWo)A@yF2$~c(nR$rSAaFQ$(Wh{vkG1AlutDMw=mM`C`T=X&|Ad9fb5Od}ROt1z zOpczHqrb4Jo^rSCiW#&o(m7jFamnrsTpQb;*h4o8r#$aZ}2RaT-x2u^^ z%u@YyIv$U^u~@9(XGbSwU@fk6SikH>j+D1jQrYTKGJpW%vUT{!d}7THI5&Sa?~MKy zS0-mvMl+BOcroEJ@hN!2H_?coTEJ5Q<;Nd?yx;eIj4{$$E2?YUO|NtNPJ-PdDf;s} zab;}Mz0kbOI}5*w@3gROcnl#5)wQnEhDBfn!Xhy`u>C}*E~vWpO^HS)FC>8^umI=+ z&H;LW6w#;EF`}vQd_9Muru`KnQVPI9U?(sD)&Dg-0j3#(!fNKVZ_GoYH{la~d*1Yh$TI-TL>mI4vpNb@sU2=IZ8vL%AXUx0 zz{K0|nK(yizLHaeW#ZhRfQXoK^}1$=$#1{Yn002ovPDHLkV1n#w+^+xt literal 0 HcmV?d00001 diff --git a/DroidFishTest/res/drawable-ldpi/ic_launcher.png b/DroidFishTest/res/drawable-ldpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..1095584ec21f71cd0afc9e0993aa2209671b590c GIT binary patch literal 1723 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 + + + + + \ No newline at end of file diff --git a/DroidFishTest/res/values/strings.xml b/DroidFishTest/res/values/strings.xml new file mode 100644 index 0000000..3a99398 --- /dev/null +++ b/DroidFishTest/res/values/strings.xml @@ -0,0 +1,7 @@ + + + + Hello World! + DroidFishTestTest + + \ No newline at end of file diff --git a/DroidFishTest/src/org/petero/droidfish/engine/BookTest.java b/DroidFishTest/src/org/petero/droidfish/engine/BookTest.java new file mode 100644 index 0000000..e39444d --- /dev/null +++ b/DroidFishTest/src/org/petero/droidfish/engine/BookTest.java @@ -0,0 +1,78 @@ +/* + DroidFish - An Android chess program. + Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package org.petero.droidfish.engine; + + +import java.util.ArrayList; + +import junit.framework.TestCase; + +import org.petero.droidfish.gamelogic.ChessParseError; +import org.petero.droidfish.gamelogic.Move; +import org.petero.droidfish.gamelogic.MoveGen; +import org.petero.droidfish.gamelogic.Position; +import org.petero.droidfish.gamelogic.TextIO; + +/** + * + * @author petero + */ +public class BookTest extends TestCase { + + public BookTest() { + } + + /** + * Test of getBookMove method, of class Book. + */ + public void testGetBookMove() throws ChessParseError { + Position pos = TextIO.readFEN(TextIO.startPosFEN); + DroidBook book = DroidBook.getInstance(); + Move move = book.getBookMove(pos); + checkValid(pos, move); + + // Test "out of book" condition + pos.setCastleMask(0); + move = book.getBookMove(pos); + assertEquals(null, move); + } + + /** + * Test of getAllBookMoves method, of class Book. + */ + public void testGetAllBookMoves() throws ChessParseError { + Position pos = TextIO.readFEN(TextIO.startPosFEN); + DroidBook book = DroidBook.getInstance(); + String moveListString = book.getAllBookMoves(pos).first; + String[] strMoves = moveListString.split(":[0-9]* "); + assertTrue(strMoves.length > 1); + for (String strMove : strMoves) { + Move m = TextIO.stringToMove(pos, strMove); + checkValid(pos, m); + } + } + + /** Check that move is a legal move in position pos. */ + private void checkValid(Position pos, Move move) { + assertTrue(move != null); + ArrayList moveList = new MoveGen().pseudoLegalMoves(pos); + moveList = MoveGen.removeIllegal(pos, moveList); + assertTrue(moveList.contains(move)); + } +} diff --git a/DroidFishTest/src/org/petero/droidfish/engine/PolyglotBookTest.java b/DroidFishTest/src/org/petero/droidfish/engine/PolyglotBookTest.java new file mode 100644 index 0000000..3f7e379 --- /dev/null +++ b/DroidFishTest/src/org/petero/droidfish/engine/PolyglotBookTest.java @@ -0,0 +1,82 @@ +/* + DroidFish - An Android chess program. + Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package org.petero.droidfish.engine; + + +import junit.framework.TestCase; + +import org.petero.droidfish.gamelogic.ChessParseError; +import org.petero.droidfish.gamelogic.Position; +import org.petero.droidfish.gamelogic.TextIO; + + +public class PolyglotBookTest extends TestCase { + public PolyglotBookTest() { + } + + /** + * Test of getBookMove method, of class Book. + */ + public void testGetHashKey() throws ChessParseError { + // starting position + Position pos = TextIO.readFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"); + long key = 0x463b96181691fc9cL; + assertEquals(key, PolyglotBook.getHashKey(pos)); + + // position after e2e4 + pos = TextIO.readFEN("rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1"); + key = 0x823c9b50fd114196L; + assertEquals(key, PolyglotBook.getHashKey(pos)); + + // position after e2e4 d75 + pos = TextIO.readFEN("rnbqkbnr/ppp1pppp/8/3p4/4P3/8/PPPP1PPP/RNBQKBNR w KQkq d6 0 2"); + key = 0x0756b94461c50fb0L; + assertEquals(key, PolyglotBook.getHashKey(pos)); + + // position after e2e4 d7d5 e4e5 + pos = TextIO.readFEN("rnbqkbnr/ppp1pppp/8/3pP3/8/8/PPPP1PPP/RNBQKBNR b KQkq - 0 2"); + key = 0x662fafb965db29d4L; + assertEquals(key, PolyglotBook.getHashKey(pos)); + + // position after e2e4 d7d5 e4e5 f7f5 + pos = TextIO.readFEN("rnbqkbnr/ppp1p1pp/8/3pPp2/8/8/PPPP1PPP/RNBQKBNR w KQkq f6 0 3"); + key = 0x22a48b5a8e47ff78L; + assertEquals(key, PolyglotBook.getHashKey(pos)); + + // position after e2e4 d7d5 e4e5 f7f5 e1e2 + pos = TextIO.readFEN("rnbqkbnr/ppp1p1pp/8/3pPp2/8/8/PPPPKPPP/RNBQ1BNR b kq - 0 3"); + key = 0x652a607ca3f242c1L; + assertEquals(key, PolyglotBook.getHashKey(pos)); + + // position after e2e4 d7d5 e4e5 f7f5 e1e2 e8f7 + pos = TextIO.readFEN("rnbq1bnr/ppp1pkpp/8/3pPp2/8/8/PPPPKPPP/RNBQ1BNR w - - 0 4"); + key = 0x00fdd303c946bdd9L; + assertEquals(key, PolyglotBook.getHashKey(pos)); + + // position after a2a4 b7b5 h2h4 b5b4 c2c4 + pos = TextIO.readFEN("rnbqkbnr/p1pppppp/8/8/PpP4P/8/1P1PPPP1/RNBQKBNR b KQkq c3 0 3"); + key = 0x3c8123ea7b067637L; + assertEquals(key, PolyglotBook.getHashKey(pos)); + + // position after a2a4 b7b5 h2h4 b5b4 c2c4 b4c3 a1a3 + pos = TextIO.readFEN("rnbqkbnr/p1pppppp/8/8/P6P/R1p5/1P1PPPP1/1NBQKBNR b Kkq - 0 4"); + key = 0x5c3f9b829b279560L; + assertEquals(key, PolyglotBook.getHashKey(pos)); + } +} diff --git a/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTest.java b/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTest.java new file mode 100644 index 0000000..40a106d --- /dev/null +++ b/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTest.java @@ -0,0 +1,461 @@ +/* + DroidFish - An Android chess program. + Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package org.petero.droidfish.gamelogic; + +import java.util.ArrayList; + +import junit.framework.TestCase; + + +/** + * + * @author petero + */ +public class GameTest extends TestCase { + + public GameTest() { + } + + /** + * Test of haveDrawOffer method, of class Game. + */ + public void testHaveDrawOffer() { + Game game = new Game(null, null, 0, 0, 0); + assertEquals(false, game.haveDrawOffer()); + + boolean res = game.processString("e4"); + assertEquals(true, res); + assertEquals(false, game.haveDrawOffer()); + + res = game.processString("draw offer e5"); + assertEquals(true, res); + assertEquals(true, game.haveDrawOffer()); + assertEquals(Game.GameState.ALIVE, game.getGameState()); // Draw offer does not imply draw + assertEquals(Piece.BPAWN, game.currPos().getPiece(Position.getSquare(4, 4))); // e5 move made + + res = game.processString("draw offer Nf3"); + assertEquals(true, res); + assertEquals(true, game.haveDrawOffer()); + assertEquals(Game.GameState.ALIVE, game.getGameState()); // Draw offer does not imply draw + assertEquals(Piece.WKNIGHT, game.currPos().getPiece(Position.getSquare(5, 2))); // Nf3 move made + + res = game.processString("Nc6"); + assertEquals(true, res); + assertEquals(false, game.haveDrawOffer()); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + assertEquals(Piece.BKNIGHT, game.currPos().getPiece(Position.getSquare(2, 5))); // Nc6 move made + + res = game.processString("draw offer Bb5"); + assertEquals(true, res); + assertEquals(true, game.haveDrawOffer()); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + assertEquals(Piece.WBISHOP, game.currPos().getPiece(Position.getSquare(1, 4))); // Bb5 move made + + res = game.processString("draw accept"); + assertEquals(true, res); + assertEquals(Game.GameState.DRAW_AGREE, game.getGameState()); // Draw by agreement + + game.undoMove(); // Undo "draw accept" + assertEquals(Piece.WBISHOP, game.currPos().getPiece(TextIO.getSquare("b5"))); + assertEquals(true, game.haveDrawOffer()); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + game.undoMove(); // Undo "Bb5" + assertEquals(Piece.EMPTY, game.currPos().getPiece(Position.getSquare(1, 4))); // Bb5 move undone + assertEquals(false, game.haveDrawOffer()); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + game.undoMove(); + assertEquals(Piece.EMPTY, game.currPos().getPiece(Position.getSquare(2, 5))); // Nc6 move undone + assertEquals(true, game.haveDrawOffer()); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + + game.redoMove(); + assertEquals(Piece.BKNIGHT, game.currPos().getPiece(Position.getSquare(2, 5))); // Nc6 move redone + assertEquals(false, game.haveDrawOffer()); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + game.redoMove(); + assertEquals(Piece.WBISHOP, game.currPos().getPiece(Position.getSquare(1, 4))); // Bb5 move redone + assertEquals(true, game.haveDrawOffer()); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + game.redoMove(); + assertEquals(Game.GameState.DRAW_AGREE, game.getGameState()); // Can redo draw accept + + // Test draw offer in connection with invalid move + game.newGame(); + assertEquals(false, game.haveDrawOffer()); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + + res = game.processString("draw offer e5"); + assertEquals(true, res); + assertEquals(TextIO.startPosFEN, TextIO.toFEN(game.currPos())); // Move invalid, not executed + res = game.processString("e4"); + assertEquals(true, res); + assertEquals(true, game.haveDrawOffer()); // Previous draw offer still valid + assertEquals(Piece.WPAWN, game.currPos().getPiece(Position.getSquare(4, 3))); // e4 move made + + // Undo/redo shall clear "pendingDrawOffer". + game.newGame(); + game.processString("e4"); + game.processString("draw offer e4"); // Invalid black move + assertEquals(true, game.pendingDrawOffer); + game.undoMove(); + game.redoMove(); + game.processString("e5"); + assertEquals(true,game.currPos().whiteMove); + assertEquals(false, game.haveDrawOffer()); + } + + /** + * Test of draw by 50 move rule, of class Game. + */ + public void testDraw50() throws ChessParseError { + Game game = new Game(null, null, 0, 0, 0); + assertEquals(false, game.haveDrawOffer()); + boolean res = game.processString("draw 50"); + assertEquals(true, res); + assertEquals(Game.GameState.ALIVE, game.getGameState()); // Draw claim invalid + res = game.processString("e4"); + assertEquals(true, game.haveDrawOffer()); // Invalid claim converted to draw offer + + String fen = "8/4k3/8/P7/8/8/8/1N2K2R w K - 99 83"; + game.setPos(TextIO.readFEN(fen)); + res = game.processString("draw 50"); + assertEquals(Game.GameState.ALIVE, game.getGameState()); // Draw claim invalid + + game.setPos(TextIO.readFEN(fen)); + game.processString("draw 50 Nc3"); + assertEquals(Game.GameState.DRAW_50, game.getGameState()); // Draw claim valid + assertEquals("Game over, draw by 50 move rule! [Nc3]", game.getGameStateString()); + + game.setPos(TextIO.readFEN(fen)); + game.processString("draw 50 a6"); + assertEquals(Game.GameState.ALIVE, game.getGameState()); // Pawn move resets counter + assertEquals(Piece.WPAWN, game.currPos().getPiece(Position.getSquare(0, 5))); // Move a6 made + + game.setPos(TextIO.readFEN(fen)); + game.processString("draw 50 O-O"); + assertEquals(Game.GameState.DRAW_50, game.getGameState()); // Castling doesn't reset counter + + game.setPos(TextIO.readFEN(fen)); + game.processString("draw 50 Kf2"); + assertEquals(Game.GameState.DRAW_50, game.getGameState()); // Loss of castling right doesn't reset counter + + game.setPos(TextIO.readFEN(fen)); + game.processString("draw 50 Ke3"); + assertEquals(Game.GameState.ALIVE, game.getGameState()); // Ke3 is invalid + assertEquals(true, game.currPos().whiteMove); + game.processString("a6"); + assertEquals(true, game.haveDrawOffer()); // Previous invalid claim converted to offer + game.processString("draw 50"); + assertEquals(Game.GameState.ALIVE, game.getGameState()); // 50 move counter reset. + res = game.processString("draw accept"); + assertEquals(true, res); + assertEquals(Game.GameState.DRAW_AGREE, game.getGameState()); // Can accept previous implicit offer + + fen = "3k4/R7/3K4/8/8/8/8/8 w - - 99 78"; + game.setPos(TextIO.readFEN(fen)); + game.processString("Ra8"); + assertEquals(Game.GameState.WHITE_MATE, game.getGameState()); + game.processString("draw 50"); + assertEquals(Game.GameState.WHITE_MATE, game.getGameState()); // Can't claim draw when game over + } + + /** + * Test of draw by repetition, of class Game. + */ + public void testDrawRep() throws ChessParseError { + Game game = new Game(null, null, 0, 0, 0); + assertEquals(false, game.haveDrawOffer()); + game.processString("Nc3"); + game.processString("Nc6"); + game.processString("Nb1"); + game.processString("Nb8"); + game.processString("Nf3"); + game.processString("Nf6"); + game.processString("Ng1"); + assertEquals(false, game.haveDrawOffer()); + game.processString("draw rep"); + assertEquals(Game.GameState.ALIVE, game.getGameState()); // Claim not valid, one more move needed + game.processString("draw rep Nc6"); + assertEquals(Game.GameState.ALIVE, game.getGameState()); // Claim not valid, wrong move claimed + assertEquals(Piece.BKNIGHT, game.currPos().getPiece(Position.getSquare(2, 5))); // Move Nc6 made + assertEquals(true, game.haveDrawOffer()); + game.undoMove(); + assertEquals(false, game.haveDrawOffer()); + assertEquals(Piece.EMPTY, game.currPos().getPiece(Position.getSquare(2, 5))); + game.processString("draw rep Ng8"); + assertEquals(Game.GameState.DRAW_REP, game.getGameState()); + assertEquals(Piece.EMPTY, game.currPos().getPiece(Position.getSquare(6, 7))); // Ng8 not played + + // Test draw by repetition when a "potential ep square but not real ep square" position is present. + game.newGame(); + game.processString("e4"); // e3 is not a real epSquare here + game.processString("Nf6"); + game.processString("Nf3"); + game.processString("Ng8"); + game.processString("Ng1"); + game.processString("Nf6"); + game.processString("Nf3"); + game.processString("Ng8"); + game.processString("draw rep Ng1"); + assertEquals(Game.GameState.DRAW_REP, game.getGameState()); + + // Now check the case when e3 *is* an epSquare + game.newGame(); + game.processString("Nf3"); + game.processString("d5"); + game.processString("Ng1"); + game.processString("d4"); + game.processString("e4"); // Here e3 is a real epSquare + game.processString("Nf6"); + game.processString("Nf3"); + game.processString("Ng8"); + game.processString("Ng1"); + game.processString("Nf6"); + game.processString("Nf3"); + game.processString("Ng8"); + game.processString("draw rep Ng1"); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + + // EP capture not valid because it would leave the king in check. Therefore + // the position has been repeated three times at the end of the move sequence. + game.setPos(TextIO.readFEN("4k2n/8/8/8/4p3/8/3P4/3KR2N w - - 0 1")); + game.processString("d4"); + game.processString("Ng6"); + game.processString("Ng3"); + game.processString("Nh8"); + game.processString("Nh1"); + game.processString("Ng6"); + game.processString("Ng3"); + game.processString("Nh8"); + game.processString("draw rep Nh1"); + assertEquals(Game.GameState.DRAW_REP, game.getGameState()); + } + + /** + * Test of draw offer/accept/request command. + */ + public void testDrawBug() throws ChessParseError { + Game game = new Game(null, null, 0, 0, 0); + assertEquals(false, game.haveDrawOffer()); + game.processString("e4"); + game.processString("c5"); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + game.processString("draw accept"); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + game.processString("draw rep"); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + game.processString("draw 50"); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + assertEquals(Piece.EMPTY, game.tree.currentPos.getPiece(TextIO.getSquare("e5"))); + } + + /** + * Test of resign command, of class Game. + */ + public void testResign() throws ChessParseError { + Game game = new Game(null, null, 0, 0, 0); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + game.processString("f3"); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + game.processString("resign"); + assertEquals(Game.GameState.RESIGN_BLACK, game.getGameState()); + game.undoMove(); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + game.processString("f3"); + game.processString("e5"); + game.processString("resign"); + assertEquals(Game.GameState.RESIGN_WHITE, game.getGameState()); + game.undoMove(); + game.processString("e5"); + game.processString("g4"); + game.processString("Qh4"); + assertEquals(Game.GameState.BLACK_MATE, game.getGameState()); + game.processString("resign"); + assertEquals(Game.GameState.BLACK_MATE, game.getGameState()); // Can't resign after game over + + String fen = "8/1p6/2rp2p1/8/p3Qqk1/6R1/PP4PK/8 b - - 3 42"; + game.setPos(TextIO.readFEN(fen)); + game.processString("resign"); + assertEquals(Game.GameState.RESIGN_BLACK, game.getGameState()); + } + + /** + * Test of processString method, of class Game. + */ + public void testProcessString() throws ChessParseError { + Game game = new Game(null, null, 0, 0, 0); + assertEquals(TextIO.startPosFEN, TextIO.toFEN(game.currPos())); + boolean res = game.processString("Nf3"); + assertEquals(true, res); + assertEquals(1, game.currPos().halfMoveClock); + assertEquals(1, game.currPos().fullMoveCounter); + res = game.processString("d5"); + assertEquals(true, res); + assertEquals(0, game.currPos().halfMoveClock); + assertEquals(2, game.currPos().fullMoveCounter); + + game.undoMove(); + assertEquals(1, game.currPos().halfMoveClock); + assertEquals(1, game.currPos().fullMoveCounter); + game.undoMove(); + assertEquals(TextIO.startPosFEN, TextIO.toFEN(game.currPos())); + game.undoMove(); + assertEquals(TextIO.startPosFEN, TextIO.toFEN(game.currPos())); + + game.redoMove(); + assertEquals(1, game.currPos().halfMoveClock); + assertEquals(1, game.currPos().fullMoveCounter); + game.redoMove(); + assertEquals(0, game.currPos().halfMoveClock); + assertEquals(2, game.currPos().fullMoveCounter); + game.redoMove(); + assertEquals(0, game.currPos().halfMoveClock); + assertEquals(2, game.currPos().fullMoveCounter); + + game.newGame(); + assertEquals(TextIO.startPosFEN, TextIO.toFEN(game.currPos())); + + String fen = "8/8/8/4k3/8/8/2p5/5K2 b - - 47 68"; + Position pos = TextIO.readFEN(fen); + game.setPos(TextIO.readFEN(fen)); + assertEquals(pos, game.currPos()); + + res = game.processString("junk"); + assertEquals(false, res); + + game.newGame(); + res = game.processString("e7e5"); + assertEquals(false, res); + } + + /** + * Test of getGameState method, of class Game. + */ + public void testGetGameState() throws ChessParseError { + Game game = new Game(null, null, 0, 0, 0); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + game.processString("f3"); + game.processString("e5"); + game.processString("g4"); + game.processString("Qh4"); + assertEquals(Game.GameState.BLACK_MATE, game.getGameState()); + + game.setPos(TextIO.readFEN("5k2/5P2/5K2/8/8/8/8/8 b - - 0 1")); + assertEquals(Game.GameState.BLACK_STALEMATE, game.getGameState()); + } + + /** + * Test of insufficientMaterial method, of class Game. + */ + public void testInsufficientMaterial() throws ChessParseError { + Game game = new Game(null, null, 0, 0, 0); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + game.setPos(TextIO.readFEN("4k3/8/8/8/8/8/8/4K3 w - - 0 1")); + assertEquals(Game.GameState.DRAW_NO_MATE, game.getGameState()); + final int a1 = Position.getSquare(0, 0); + Position pos = new Position(game.currPos()); + pos.setPiece(a1, Piece.WROOK); game.setPos(pos); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + pos.setPiece(a1, Piece.BQUEEN); game.setPos(pos); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + pos.setPiece(a1, Piece.WPAWN); game.setPos(pos); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + pos.setPiece(a1, Piece.BKNIGHT); game.setPos(pos); + assertEquals(Game.GameState.DRAW_NO_MATE, game.getGameState()); + pos.setPiece(a1, Piece.WBISHOP); game.setPos(pos); + assertEquals(Game.GameState.DRAW_NO_MATE, game.getGameState()); + + final int c1 = Position.getSquare(2, 0); + pos.setPiece(c1, Piece.WKNIGHT); game.setPos(pos); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + pos.setPiece(c1, Piece.BBISHOP); game.setPos(pos); + assertEquals(Game.GameState.DRAW_NO_MATE, game.getGameState()); + pos.setPiece(c1, Piece.WBISHOP); game.setPos(pos); + assertEquals(Game.GameState.DRAW_NO_MATE, game.getGameState()); + + final int b2 = Position.getSquare(1, 1); + pos.setPiece(b2, Piece.WBISHOP); game.setPos(pos); + assertEquals(Game.GameState.DRAW_NO_MATE, game.getGameState()); + pos.setPiece(b2, Piece.BBISHOP); game.setPos(pos); + assertEquals(Game.GameState.DRAW_NO_MATE, game.getGameState()); + + final int b3 = Position.getSquare(1, 2); + pos.setPiece(b3, Piece.WBISHOP); game.setPos(pos); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + + // Can't force mate with KNNK, but still not an automatic draw. + game.setPos(TextIO.readFEN("8/8/8/8/8/8/8/K3nnk1 w - - 0 1")); + assertEquals(Game.GameState.ALIVE, game.getGameState()); + } + + /** Test that UCI history is not longer than necessary. + * We can't expect engines to handle null moves, for example. */ + public void testUCIHistory() throws ChessParseError { + Game game = new Game(null, null, 0, 0, 0); + + Pair> hist = game.getUCIHistory(); + assertEquals(0, hist.second.size()); + Position expectedPos = new Position(game.currPos()); + assertEquals(expectedPos, hist.first); + + game.processString("Nf3"); + hist = game.getUCIHistory(); + assertEquals(1, hist.second.size()); + assertEquals(TextIO.UCIstringToMove("g1f3"), hist.second.get(0)); + assertEquals(expectedPos, hist.first); + + game.processString("e5"); + hist = game.getUCIHistory(); + expectedPos = new Position(game.currPos()); + assertEquals(0, hist.second.size()); + assertEquals(expectedPos, hist.first); + + game.processString("Nc3"); + hist = game.getUCIHistory(); + assertEquals(1, hist.second.size()); + assertEquals(TextIO.UCIstringToMove("b1c3"), hist.second.get(0)); + assertEquals(expectedPos, hist.first); + + game.processString("Nc6"); + hist = game.getUCIHistory(); + assertEquals(2, hist.second.size()); + assertEquals(TextIO.UCIstringToMove("b1c3"), hist.second.get(0)); + assertEquals(TextIO.UCIstringToMove("b8c6"), hist.second.get(1)); + assertEquals(expectedPos, hist.first); + + game.processString("--"); + hist = game.getUCIHistory(); + expectedPos = new Position(game.currPos()); + assertEquals(0, hist.second.size()); + assertEquals(expectedPos, hist.first); + + game.processString("Nf6"); + hist = game.getUCIHistory(); + assertEquals(1, hist.second.size()); + assertEquals(TextIO.UCIstringToMove("g8f6"), hist.second.get(0)); + assertEquals(expectedPos, hist.first); + + for (int i = 0; i < 6; i++) + game.undoMove(); + hist = game.getUCIHistory(); + assertEquals(0, hist.second.size()); + expectedPos = TextIO.readFEN(TextIO.startPosFEN); + assertEquals(expectedPos, hist.first); + } +} diff --git a/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTreeTest.java b/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTreeTest.java new file mode 100644 index 0000000..becb87c --- /dev/null +++ b/DroidFishTest/src/org/petero/droidfish/gamelogic/GameTreeTest.java @@ -0,0 +1,651 @@ +/* + DroidFish - An Android chess program. + Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package org.petero.droidfish.gamelogic; + + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +import org.petero.droidfish.PGNOptions; +import org.petero.droidfish.gamelogic.Game.GameState; +import org.petero.droidfish.gamelogic.GameTree.Node; +import org.petero.droidfish.gamelogic.GameTree.PgnScanner; + +public class GameTreeTest extends TestCase { + + public final void testGameTree() throws ChessParseError { + GameTree gt = new GameTree(null); + Position expectedPos = TextIO.readFEN(TextIO.startPosFEN); + assertEquals(expectedPos, gt.currentPos); + + List varList = gt.variations(); + assertEquals(0, varList.size()); + + int varNo = gt.addMove("e4", "", 0, "", ""); + assertEquals(0, varNo); + assertEquals(expectedPos, gt.currentPos); + + gt.goForward(varNo); + Move move = TextIO.UCIstringToMove("e2e4"); + UndoInfo ui = new UndoInfo(); + expectedPos.makeMove(move, ui); + assertEquals(expectedPos, gt.currentPos); + + gt.goBack(); + expectedPos.unMakeMove(move, ui); + assertEquals(expectedPos, gt.currentPos); + + varNo = gt.addMove("d4", "", 0, "", ""); + assertEquals(1, varNo); + assertEquals(expectedPos, gt.currentPos); + varList = gt.variations(); + assertEquals(2, varList.size()); + + gt.goForward(varNo); + move = TextIO.UCIstringToMove("d2d4"); + expectedPos.makeMove(move, ui); + assertEquals(expectedPos, gt.currentPos); + + varNo = gt.addMove("g8f6", "", 0, "", ""); + assertEquals(0, varNo); + assertEquals(expectedPos, gt.currentPos); + varList = gt.variations(); + assertEquals(1, varList.size()); + + gt.goForward(-1); + Move move2 = TextIO.UCIstringToMove("g8f6"); + UndoInfo ui2 = new UndoInfo(); + expectedPos.makeMove(move2, ui2); + assertEquals(expectedPos, gt.currentPos); + assertEquals("Nf6", gt.currentNode.moveStr); + + gt.goBack(); + assertEquals("d4", gt.currentNode.moveStr); + gt.goBack(); + expectedPos.unMakeMove(move2, ui2); + expectedPos.unMakeMove(move, ui); + assertEquals(expectedPos, gt.currentPos); + assertEquals("", gt.currentNode.moveStr); + + gt.goForward(-1); // Should remember that d2d4 was last visited branch + expectedPos.makeMove(move, ui); + assertEquals(expectedPos, gt.currentPos); + + byte[] serialState = gt.toByteArray(); + gt = new GameTree(null); + gt.fromByteArray(serialState); + assertEquals(expectedPos, gt.currentPos); + + gt.goBack(); + expectedPos.unMakeMove(move, ui); + assertEquals(expectedPos, gt.currentPos); + varList = gt.variations(); + assertEquals(2, varList.size()); + } + + private final String getMoveListAsString(GameTree gt) { + StringBuilder ret = new StringBuilder(); + Pair, Integer> ml = gt.getMoveList(); + List lst = ml.first; + final int numMovesPlayed = ml.second; + for (int i = 0; i < lst.size(); i++) { + if (i == numMovesPlayed) + ret.append('*'); + if (i > 0) + ret.append(' '); + ret.append(lst.get(i).moveStr); + } + if (lst.size() == numMovesPlayed) + ret.append('*'); + return ret.toString(); + } + + public final void testGetMoveList() throws ChessParseError { + GameTree gt = new GameTree(null); + gt.addMove("e4", "", 0, "", ""); + gt.addMove("d4", "", 0, "", ""); + assertEquals("*e4", getMoveListAsString(gt)); + + gt.goForward(0); + assertEquals("e4*", getMoveListAsString(gt)); + + gt.addMove("e5", "", 0, "", ""); + gt.addMove("c5", "", 0, "", ""); + assertEquals("e4* e5", getMoveListAsString(gt)); + + gt.goForward(1); + assertEquals("e4 c5*", getMoveListAsString(gt)); + + gt.addMove("Nf3", "", 0, "", ""); + gt.addMove("d4", "", 0, "", ""); + assertEquals("e4 c5* Nf3", getMoveListAsString(gt)); + + gt.goForward(1); + assertEquals("e4 c5 d4*", getMoveListAsString(gt)); + + gt.goBack(); + assertEquals("e4 c5* d4", getMoveListAsString(gt)); + + gt.goBack(); + assertEquals("e4* c5 d4", getMoveListAsString(gt)); + + gt.goBack(); + assertEquals("*e4 c5 d4", getMoveListAsString(gt)); + + gt.goForward(1); + assertEquals("d4*", getMoveListAsString(gt)); + + gt.goBack(); + assertEquals("*d4", getMoveListAsString(gt)); + + gt.goForward(0); + assertEquals("e4* c5 d4", getMoveListAsString(gt)); + } + + public final void testReorderVariation() throws ChessParseError { + GameTree gt = new GameTree(null); + gt.addMove("e4", "", 0, "", ""); + gt.addMove("d4", "", 0, "", ""); + gt.addMove("c4", "", 0, "", ""); + assertEquals("e4 d4 c4", getVariationsAsString(gt)); + assertEquals(0, gt.currentNode.defaultChild); + + gt.reorderVariation(1, 0); + assertEquals("d4 e4 c4", getVariationsAsString(gt)); + assertEquals(1, gt.currentNode.defaultChild); + + gt.reorderVariation(0, 2); + assertEquals("e4 c4 d4", getVariationsAsString(gt)); + assertEquals(0, gt.currentNode.defaultChild); + + gt.reorderVariation(1, 2); + assertEquals("e4 d4 c4", getVariationsAsString(gt)); + assertEquals(0, gt.currentNode.defaultChild); + + gt.reorderVariation(0, 1); + assertEquals("d4 e4 c4", getVariationsAsString(gt)); + assertEquals(1, gt.currentNode.defaultChild); + } + + public final void testDeleteVariation() throws ChessParseError { + GameTree gt = new GameTree(null); + gt.addMove("e4", "", 0, "", ""); + gt.addMove("d4", "", 0, "", ""); + gt.addMove("c4", "", 0, "", ""); + gt.addMove("f4", "", 0, "", ""); + gt.deleteVariation(0); + assertEquals("d4 c4 f4", getVariationsAsString(gt)); + assertEquals(0, gt.currentNode.defaultChild); + + gt.reorderVariation(0, 2); + assertEquals("c4 f4 d4", getVariationsAsString(gt)); + assertEquals(2, gt.currentNode.defaultChild); + gt.deleteVariation(1); + assertEquals("c4 d4", getVariationsAsString(gt)); + assertEquals(1, gt.currentNode.defaultChild); + + gt.addMove("g4", "", 0, "", ""); + gt.addMove("h4", "", 0, "", ""); + assertEquals("c4 d4 g4 h4", getVariationsAsString(gt)); + assertEquals(1, gt.currentNode.defaultChild); + gt.reorderVariation(1, 2); + assertEquals("c4 g4 d4 h4", getVariationsAsString(gt)); + assertEquals(2, gt.currentNode.defaultChild); + gt.deleteVariation(2); + assertEquals("c4 g4 h4", getVariationsAsString(gt)); + assertEquals(0, gt.currentNode.defaultChild); + } + + private final String getVariationsAsString(GameTree gt) { + StringBuilder ret = new StringBuilder(); + List vars = gt.variations(); + for (int i = 0; i < vars.size(); i++) { + if (i > 0) + ret.append(' '); + String moveStr = TextIO.moveToString(gt.currentPos, vars.get(i), false); + ret.append(moveStr); + } + return ret.toString(); + } + + public final void testGetRemainingTime() throws ChessParseError { + GameTree gt = new GameTree(null); + int initialTime = 60000; + assertEquals(initialTime, gt.getRemainingTime(true, initialTime)); + assertEquals(initialTime, gt.getRemainingTime(false, initialTime)); + + gt.addMove("e4", "", 0, "", ""); + gt.goForward(-1); + assertEquals(initialTime, gt.getRemainingTime(true, initialTime)); + assertEquals(initialTime, gt.getRemainingTime(false, initialTime)); + gt.setRemainingTime(45000); + assertEquals(45000, gt.getRemainingTime(true, initialTime)); + assertEquals(initialTime, gt.getRemainingTime(false, initialTime)); + + gt.addMove("e5", "", 0, "", ""); + assertEquals(45000, gt.getRemainingTime(true, initialTime)); + assertEquals(initialTime, gt.getRemainingTime(false, initialTime)); + + gt.goForward(-1); + assertEquals(45000, gt.getRemainingTime(true, initialTime)); + assertEquals(initialTime, gt.getRemainingTime(false, initialTime)); + + gt.addMove("Nf3", "", 0, "", ""); + gt.goForward(-1); + gt.addMove("Nc6", "", 0, "", ""); + gt.goForward(-1); + assertEquals(45000, gt.getRemainingTime(true, initialTime)); + assertEquals(initialTime, gt.getRemainingTime(false, initialTime)); + + gt.setRemainingTime(30000); + assertEquals(45000, gt.getRemainingTime(true, initialTime)); + assertEquals(30000, gt.getRemainingTime(false, initialTime)); + + gt.addMove("Bb5", "", 0, "", ""); + gt.goForward(-1); + gt.setRemainingTime(20000); + assertEquals(20000, gt.getRemainingTime(true, initialTime)); + assertEquals(30000, gt.getRemainingTime(false, initialTime)); + + gt.addMove("a6", "", 0, "", ""); + gt.goForward(-1); + gt.setRemainingTime(15000); + assertEquals(20000, gt.getRemainingTime(true, initialTime)); + assertEquals(15000, gt.getRemainingTime(false, initialTime)); + + gt.goBack(); + assertEquals(20000, gt.getRemainingTime(true, initialTime)); + assertEquals(30000, gt.getRemainingTime(false, initialTime)); + + gt.goBack(); + assertEquals(45000, gt.getRemainingTime(true, initialTime)); + assertEquals(30000, gt.getRemainingTime(false, initialTime)); + + gt.goBack(); + assertEquals(45000, gt.getRemainingTime(true, initialTime)); + assertEquals(initialTime, gt.getRemainingTime(false, initialTime)); + + gt.goBack(); + gt.goBack(); + gt.goBack(); + assertEquals(initialTime, gt.getRemainingTime(true, initialTime)); + assertEquals(initialTime, gt.getRemainingTime(false, initialTime)); + } + + private final List getAllTokens(String s) { + PgnScanner sc = new PgnScanner(s); + List ret = new ArrayList(); + while (true) { + PgnToken tok = sc.nextToken(); + if (tok.type == PgnToken.EOF) + break; + ret.add(tok); + } + return ret; + } + + public final void testPgnScanner() throws ChessParseError { + List lst = getAllTokens("a\nb\n%junk\nc3"); // a b c3 + assertEquals(3, lst.size()); + assertEquals(PgnToken.SYMBOL, lst.get(0).type); + assertEquals("a", lst.get(0).token); + assertEquals(PgnToken.SYMBOL, lst.get(1).type); + assertEquals("b", lst.get(1).token); + assertEquals(PgnToken.SYMBOL, lst.get(2).type); + assertEquals("c3", lst.get(2).token); + + lst = getAllTokens("e2 ; e5\nc5"); // e2 comment c5 + assertEquals(3, lst.size()); + assertEquals("e2", lst.get(0).token); + assertEquals(PgnToken.COMMENT, lst.get(1).type); + assertEquals(" e5", lst.get(1).token); + assertEquals("c5", lst.get(2).token); + + lst = getAllTokens("e4?? { comment ; } e5!?"); // e4?? comment e5!? + assertEquals(3, lst.size()); + assertEquals("e4??", lst.get(0).token); + assertEquals(" comment ; ", lst.get(1).token); + assertEquals("e5!?", lst.get(2).token); + + lst = getAllTokens("e4! { comment { } e5?"); // e4! comment e5? + assertEquals(3, lst.size()); + assertEquals("e4!", lst.get(0).token); + assertEquals(" comment { ", lst.get(1).token); + assertEquals("e5?", lst.get(2).token); + + lst = getAllTokens("e4(c4 {(()\\} c5 ( e5))Nf6"); // e4 ( c4 comment c5 ( e5 ) ) Nf6 + assertEquals(10, lst.size()); + assertEquals("e4", lst.get(0).token); + assertEquals(PgnToken.LEFT_PAREN, lst.get(1).type); + assertEquals("c4", lst.get(2).token); + assertEquals("(()\\", lst.get(3).token); + assertEquals("c5", lst.get(4).token); + assertEquals(PgnToken.LEFT_PAREN, lst.get(5).type); + assertEquals("e5", lst.get(6).token); + assertEquals(PgnToken.RIGHT_PAREN, lst.get(7).type); + assertEquals(PgnToken.RIGHT_PAREN, lst.get(8).type); + assertEquals("Nf6", lst.get(9).token); + + lst = getAllTokens("[a \"string\"]"); // [ a string ] + assertEquals(4, lst.size()); + assertEquals(PgnToken.LEFT_BRACKET, lst.get(0).type); + assertEquals("a", lst.get(1).token); + assertEquals(PgnToken.STRING, lst.get(2).type); + assertEquals("string", lst.get(2).token); + assertEquals(PgnToken.RIGHT_BRACKET, lst.get(3).type); + + lst = getAllTokens("[a \"str\\\"in\\\\g\"]"); // [ a str"in\g ] + assertEquals(4, lst.size()); + assertEquals(PgnToken.LEFT_BRACKET, lst.get(0).type); + assertEquals("a", lst.get(1).token); + assertEquals(PgnToken.STRING, lst.get(2).type); + assertEquals("str\"in\\g", lst.get(2).token); + assertEquals(PgnToken.RIGHT_BRACKET, lst.get(3).type); + + lst = getAllTokens("1...Nf6$23Nf3 12 e4_+#=:-*"); // 1 . . . Nf6 $23 Nf3 12 e4_+#=:- * + assertEquals(10, lst.size()); + assertEquals(PgnToken.INTEGER, lst.get(0).type); + assertEquals("1", lst.get(0).token); + assertEquals(PgnToken.PERIOD, lst.get(1).type); + assertEquals(PgnToken.PERIOD, lst.get(2).type); + assertEquals(PgnToken.PERIOD, lst.get(3).type); + assertEquals("Nf6", lst.get(4).token); + assertEquals(PgnToken.NAG, lst.get(5).type); + assertEquals("23", lst.get(5).token); + assertEquals("Nf3", lst.get(6).token); + assertEquals(PgnToken.INTEGER, lst.get(7).type); + assertEquals("12", lst.get(7).token); + assertEquals("e4_+#=:-", lst.get(8).token); + assertEquals(PgnToken.ASTERISK, lst.get(9).type); + + lst = getAllTokens("1/2-1/2 1-0 0-1"); + assertEquals(3, lst.size()); + assertEquals(PgnToken.SYMBOL, lst.get(0).type); + assertEquals("1/2-1/2", lst.get(0).token); + assertEquals(PgnToken.SYMBOL, lst.get(1).type); + assertEquals("1-0", lst.get(1).token); + assertEquals(PgnToken.SYMBOL, lst.get(2).type); + assertEquals("0-1", lst.get(2).token); + + // Test invalid data, unterminated tokens + lst = getAllTokens("e4 e5 ; ( )"); // e4 e5 comment + assertEquals(3, lst.size()); + assertEquals(PgnToken.SYMBOL, lst.get(0).type); + assertEquals("e4", lst.get(0).token); + assertEquals(PgnToken.SYMBOL, lst.get(1).type); + assertEquals("e5", lst.get(1).token); + assertEquals(PgnToken.COMMENT, lst.get(2).type); + assertEquals(" ( )", lst.get(2).token); + + lst = getAllTokens("e4 e5 {"); // e4 e5 ? + assertTrue(lst.size() >= 2); + assertEquals(PgnToken.SYMBOL, lst.get(0).type); + assertEquals("e4", lst.get(0).token); + assertEquals(PgnToken.SYMBOL, lst.get(1).type); + assertEquals("e5", lst.get(1).token); + + lst = getAllTokens("e4 e5 \""); // e4 e5 ? + assertTrue(lst.size() >= 2); + assertEquals(PgnToken.SYMBOL, lst.get(0).type); + assertEquals("e4", lst.get(0).token); + assertEquals(PgnToken.SYMBOL, lst.get(1).type); + assertEquals("e5", lst.get(1).token); + + // Test that reading beyond EOF produces more EOF tokens + PgnScanner sc = new PgnScanner("e4 e5"); + assertEquals(PgnToken.SYMBOL, sc.nextToken().type); + assertEquals(PgnToken.SYMBOL, sc.nextToken().type); + assertEquals(PgnToken.EOF, sc.nextToken().type); + assertEquals(PgnToken.EOF, sc.nextToken().type); + assertEquals(PgnToken.EOF, sc.nextToken().type); + } + + public final void testReadPGN() throws ChessParseError { + GameTree gt = new GameTree(null); + PGNOptions options = new PGNOptions(); + options.imp.variations = true; + options.imp.comments = true; + options.imp.nag = true; + boolean res = gt.readPGN("", options); + assertEquals(false, res); + + res = gt.readPGN("[White \"a\"][Black \"b\"] {comment} e4 {x}", options); + assertEquals(true, res); + assertEquals("a", gt.white); + assertEquals("b", gt.black); + assertEquals("e4", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals("comment", gt.currentNode.preComment); + assertEquals("x", gt.currentNode.postComment); + + res = gt.readPGN("e4 e5 Nf3", options); + assertEquals(true, res); + assertEquals("e4", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals("e5", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals("Nf3", getVariationsAsString(gt)); + + res = gt.readPGN("e4 e5 (c5 (c6) d4) (d5) Nf3", options); + assertEquals(true, res); + assertEquals("e4", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals("e5 c5 c6 d5", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals("Nf3", getVariationsAsString(gt)); + + res = gt.readPGN("e4 e5 (c5 (c3) d4 (Nc3)) (d5) Nf3", options); // c3 invalid, should be removed + assertEquals(true, res); + assertEquals("e4", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals("e5 c5 d5", getVariationsAsString(gt)); + gt.goForward(1); + assertEquals("d4 Nc3", getVariationsAsString(gt)); + + res = gt.readPGN("e4 + e5", options); // Extra + should be ignored + assertEquals(true, res); + assertEquals("e4", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals("e5", getVariationsAsString(gt)); + + // Test for broken PGN headers: [White "A "good" player"] + res = gt.readPGN("[White \"A \"good\" player\"]\ne4", options); + assertEquals(true, res); + assertEquals("A \"good\" player", gt.white); + assertEquals("e4", getVariationsAsString(gt)); + + // Test for broken PGN headers: [White "A "good" player"] + res = gt.readPGN("[White \"A \"good old\" player\"]\ne4", options); + assertEquals(true, res); + assertEquals("A \"good old\" player", gt.white); + assertEquals("e4", getVariationsAsString(gt)); + } + + public final void testStringEscape() throws ChessParseError { + GameTree gt = new GameTree(null); + PGNOptions options = new PGNOptions(); + gt.white = "test \"x\""; + String pgn = gt.toPGN(options); + gt.white = ""; + boolean res = gt.readPGN(pgn, options); + assertEquals(true, res); + assertEquals("test \"x\"", gt.white); + } + + public final void testNAG() throws ChessParseError { + GameTree gt = new GameTree(null); + PGNOptions options = new PGNOptions(); + options.imp.variations = true; + options.imp.comments = true; + options.imp.nag = true; + boolean res = gt.readPGN("e4! e5 ? Nf3?! Nc6 !? Bb5!! a6?? Ba4 $14", options); + assertEquals(true, res); + + assertEquals("e4", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals(1, gt.currentNode.nag); + + assertEquals("e5", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals(2, gt.currentNode.nag); + + assertEquals("Nf3", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals(6, gt.currentNode.nag); + + assertEquals("Nc6", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals(5, gt.currentNode.nag); + + assertEquals("Bb5", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals(3, gt.currentNode.nag); + + assertEquals("a6", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals(4, gt.currentNode.nag); + + assertEquals("Ba4", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals(14, gt.currentNode.nag); + } + + public final void testTime() throws ChessParseError { + GameTree gt = new GameTree(null); + PGNOptions options = new PGNOptions(); + options.imp.variations = true; + options.imp.comments = true; + options.imp.nag = true; + boolean res = gt.readPGN("e4 { x [%clk 0:0:43] y} e5 {[%clk\n1:2:3]} Nf3 Nc6 {[%clk -1:2 ]}", options); + assertEquals(true, res); + + assertEquals("e4", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals(43000, gt.currentNode.remainingTime); + assertEquals(" x y", gt.currentNode.postComment); + + assertEquals("e5", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals(((1*60+2)*60+3)*1000, gt.currentNode.remainingTime); + assertEquals("", gt.currentNode.postComment); + + assertEquals("Nf3", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals(Integer.MIN_VALUE, gt.currentNode.remainingTime); + assertEquals("", gt.currentNode.postComment); + + assertEquals("Nc6", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals(-(1*60+2)*1000, gt.currentNode.remainingTime); + assertEquals("", gt.currentNode.postComment); + } + + public final void testPlayerAction() throws ChessParseError { + GameTree gt = new GameTree(null); + int varNo = gt.addMove("--", "resign", 0, "", ""); + assertEquals(0, varNo); + + PGNOptions options = new PGNOptions(); + + String pgn = gt.toPGN(options); + assertEquals(-1, pgn.indexOf("--")); + + options.exp.playerAction = true; + pgn = gt.toPGN(options); + assertTrue(pgn.indexOf("--") >= 0); + + gt = new GameTree(null); + gt.readPGN(pgn, options); + assertEquals("--", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals(GameState.RESIGN_WHITE, gt.getGameState()); + + gt = new GameTree(null); + gt.readPGN("1. -- {[%playeraction resign]}", options); + assertEquals("--", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals(GameState.RESIGN_WHITE, gt.getGameState()); + + gt.readPGN("1. e4 -- {[%playeraction resign]}", options); + assertEquals("e4", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals("--", getVariationsAsString(gt)); + gt.goForward(0); + assertEquals(GameState.RESIGN_BLACK, gt.getGameState()); + + // Even if playerActions are not exported, moves corresponding + // to draw offers must still be exported + gt = new GameTree(null); + varNo = gt.addMove("e4", "draw offer", 0, "", ""); + assertEquals(0, varNo); + assertEquals("e4", getVariationsAsString(gt)); + gt.goForward(0); + varNo = gt.addMove("e5", "", 0, "", ""); + assertEquals(0, varNo); + assertEquals("e5", getVariationsAsString(gt)); + gt.goForward(0); + options.exp.playerAction = false; + pgn = gt.toPGN(options); + assertTrue(pgn.indexOf("e4") >= 0); + } + + public final void testGameResult() throws ChessParseError { + GameTree gt = new GameTree(null); + int varNo = gt.addMove("e4", "", 0, "", ""); + gt.goForward(varNo); + varNo = gt.addMove("e5", "", 0, "", ""); + gt.goForward(varNo); + varNo = gt.addMove("--", "resign", 0, "", ""); + gt.goBack(); + gt.goBack(); + varNo = gt.addMove("d4", "", 0, "", ""); + gt.goForward(varNo); + varNo = gt.addMove("--", "resign", 0, "", ""); + gt.goForward(varNo); + + // Black has resigned in a variation, but white resigned in the mainline, + // so the PGN result should be "0-1"; + PGNOptions options = new PGNOptions(); + options.exp.variations = true; +// options.exp.playerAction = true; + String pgn = gt.toPGN(options); + assertTrue(pgn.indexOf("1-0") < 0); + assertTrue(pgn.indexOf("0-1") >= 0); + } + + public final void testPGNPromotion() throws ChessParseError { + // PGN standard specifies that promotion moves shall have an = sign + // before the promotion piece + GameTree gt = new GameTree(null); + gt.setStartPos(TextIO.readFEN("rnbqkbnr/ppPppppp/8/8/8/8/PP1PPPPP/RNBQKBNR w KQkq - 0 1")); + int varNo = gt.addMove("cxb8N", "", 0, "", ""); + assertEquals(0, varNo); + varNo = gt.addMove("cxd8R+", "", 0, "", ""); + assertEquals(1, varNo); + assertEquals("cxb8N cxd8R+", getVariationsAsString(gt)); // Normal short alg notation does not have = + PGNOptions options = new PGNOptions(); + options.exp.variations = true; + String pgn = gt.toPGN(options); + assertTrue(pgn.indexOf("cxb8=N") >= 0); // ... but PGN promotions do have the = sign + assertTrue(pgn.indexOf("cxd8=R+") >= 0); + + } +} diff --git a/DroidFishTest/src/org/petero/droidfish/gamelogic/MoveGenTest.java b/DroidFishTest/src/org/petero/droidfish/gamelogic/MoveGenTest.java new file mode 100644 index 0000000..556698f --- /dev/null +++ b/DroidFishTest/src/org/petero/droidfish/gamelogic/MoveGenTest.java @@ -0,0 +1,204 @@ +/* + DroidFish - An Android chess program. + Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package org.petero.droidfish.gamelogic; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +/** + * + * @author petero + */ +public class MoveGenTest extends TestCase { + + public MoveGenTest() { + } + + /** + * Test of pseudoLegalMoves method, of class MoveGen. + */ + public void testPseudoLegalMoves() throws ChessParseError { + String fen = "8/3k4/8/2n2pP1/1P6/1NB5/2QP4/R3K2R w KQ f6 0 2"; + Position pos = TextIO.readFEN(fen); + assertEquals(fen, TextIO.toFEN(pos)); + List strMoves = getMoveList(pos, false); + assertTrue(strMoves.contains("Ra1-d1")); + assertTrue(!strMoves.contains("Ra1-e1")); + assertTrue(!strMoves.contains("Ra1-f1")); + assertTrue(strMoves.contains("Ra1-a7+")); + assertTrue(strMoves.contains("Ke1-f2")); + assertTrue(!strMoves.contains("Ke1-g3")); + assertTrue(strMoves.contains("Bc3-f6")); + assertTrue(!strMoves.contains("Nb3xd2")); + + // Test castling + assertTrue(strMoves.contains("O-O")); + assertTrue(strMoves.contains("O-O-O")); + assertEquals(49, strMoves.size()); + + pos.setPiece(Position.getSquare(4,3), Piece.BROOK); + strMoves = getMoveList(pos, false); + assertTrue(!strMoves.contains("O-O")); // In check, not castling possible + assertTrue(!strMoves.contains("O-O-O")); + + pos.setPiece(Position.getSquare(4, 3), Piece.EMPTY); + pos.setPiece(Position.getSquare(5, 3), Piece.BROOK); + strMoves = getMoveList(pos, false); + assertTrue(!strMoves.contains("O-O")); // f1 attacked, short castle not possible + assertTrue(strMoves.contains("O-O-O")); + + pos.setPiece(Position.getSquare(5, 3), Piece.EMPTY); + pos.setPiece(Position.getSquare(6, 3), Piece.BBISHOP); + strMoves = getMoveList(pos, false); + assertTrue(strMoves.contains("O-O")); // d1 attacked, long castle not possible + assertTrue(!strMoves.contains("O-O-O")); + + pos.setPiece(Position.getSquare(6, 3), Piece.EMPTY); + pos.setCastleMask(1 << Position.A1_CASTLE); + strMoves = getMoveList(pos, false); + assertTrue(!strMoves.contains("O-O")); // short castle right has been lost + assertTrue(strMoves.contains("O-O-O")); + } + + /** + * Test of pseudoLegalMoves method, of class MoveGen. Pawn moves. + */ + public void testPawnMoves() throws ChessParseError { + String fen = "1r2k3/P1pppp1p/8/1pP3p1/1nPp2P1/n4p1P/1P2PP2/4KBNR w K b6 0 1"; + Position pos = TextIO.readFEN(fen); + assertEquals(fen, TextIO.toFEN(pos)); + List strMoves = getMoveList(pos, false); + assertTrue(strMoves.contains("c5xb6")); // En passant capture + assertTrue(strMoves.contains("a7-a8Q")); // promotion + assertTrue(strMoves.contains("a7-a8N")); // under promotion + assertTrue(strMoves.contains("a7xb8R#")); // capture promotion + assertTrue(strMoves.contains("b2-b3")); // pawn single move + assertTrue(strMoves.contains("b2xa3")); // pawn capture to the left + assertTrue(strMoves.contains("e2-e4")); // pawn double move + assertTrue(strMoves.contains("e2xf3")); // pawn capture to the right + assertEquals(22, strMoves.size()); + + pos.setEpSquare(-1); + strMoves = getMoveList(pos, false); + assertEquals(21, strMoves.size()); // No ep, one less move possible + + // Check black pawn moves + pos.setWhiteMove(false); + strMoves = getMoveList(pos, false); + assertTrue(strMoves.contains("f3xe2")); + assertTrue(strMoves.contains("d4-d3")); + assertTrue(strMoves.contains("e7-e6")); + assertTrue(strMoves.contains("e7-e5")); + assertEquals(28, strMoves.size()); + + // Check black pawn promotion + pos.setPiece(Position.getSquare(0,1), Piece.BPAWN); + strMoves = getMoveList(pos, false); + assertTrue(strMoves.contains("a2-a1Q+")); + assertTrue(strMoves.contains("a2-a1R+")); + assertTrue(strMoves.contains("a2-a1N")); + assertTrue(strMoves.contains("a2-a1B")); + } + + /** + * Test of inCheck method, of class MoveGen. + */ + public void testInCheck() { + Position pos = new Position(); + pos.setPiece(Position.getSquare(4,2), Piece.WKING); + pos.setPiece(Position.getSquare(4,7), Piece.BKING); + assertEquals(false, MoveGen.inCheck(pos)); + + pos.setPiece(Position.getSquare(3,3), Piece.BQUEEN); + assertEquals(true, MoveGen.inCheck(pos)); + pos.setPiece(Position.getSquare(3,3), Piece.BROOK); + assertEquals(false, MoveGen.inCheck(pos)); + pos.setPiece(Position.getSquare(3,3), Piece.BPAWN); + assertEquals(true, MoveGen.inCheck(pos)); + + pos.setPiece(Position.getSquare(3,3), Piece.EMPTY); + pos.setPiece(Position.getSquare(5,3), Piece.WQUEEN); + assertEquals(false, MoveGen.inCheck(pos)); + + pos.setPiece(Position.getSquare(4, 6), Piece.BROOK); + assertEquals(true, MoveGen.inCheck(pos)); + pos.setPiece(Position.getSquare(4, 4), Piece.WPAWN); + assertEquals(false, MoveGen.inCheck(pos)); + + pos.setPiece(Position.getSquare(2, 3), Piece.BKNIGHT); + assertEquals(true, MoveGen.inCheck(pos)); + + pos.setPiece(Position.getSquare(2, 3), Piece.EMPTY); + pos.setPiece(Position.getSquare(0, 4), Piece.BKNIGHT); + assertEquals(false, MoveGen.inCheck(pos)); + } + + /** + * Test of removeIllegal method, of class MoveGen. + */ + public void testRemoveIllegal() throws ChessParseError { + Position pos = TextIO.readFEN("8/3k4/8/2n1rpP1/1P6/1NB5/2QP4/R3K2R w KQ f6 0 1"); + List strMoves = getMoveList(pos, true); + assertTrue(strMoves.contains("Qc2-e4")); + assertTrue(strMoves.contains("Bc3xe5")); + assertTrue(strMoves.contains("Ke1-d1")); + assertTrue(strMoves.contains("Ke1-f1")); + assertTrue(strMoves.contains("Ke1-f2")); + assertEquals(5, strMoves.size()); + } + + /** + * Test that if king capture is possible, only a king capture move is returned in the move list. + */ + public void testKingCapture() throws ChessParseError { + Position pos = TextIO.readFEN("8/4k3/8/8/8/8/8/4RK2 b - - 0 1"); + pos.setWhiteMove(true); + List strMoves = getMoveList(pos, false); + assertEquals(1, strMoves.size()); + assertEquals("Re1xe7", strMoves.get(0)); + + pos.setPiece(Position.getSquare(0, 2), Piece.WBISHOP); + pos.setPiece(Position.getSquare(4, 1), Piece.WPAWN); + strMoves = getMoveList(pos, false); + assertEquals(1, strMoves.size()); + assertEquals("Ba3xe7", strMoves.get(0)); + + pos.setPiece(Position.getSquare(1, 3), Piece.WPAWN); + pos.setPiece(Position.getSquare(5, 5), Piece.WPAWN); + strMoves = getMoveList(pos, false); + assertEquals(1, strMoves.size()); + assertEquals("f6xe7", strMoves.get(0)); + } + + private List getMoveList(Position pos, boolean onlyLegal) { + ArrayList moves = new MoveGen().pseudoLegalMoves(pos); + if (onlyLegal) { + moves = MoveGen.removeIllegal(pos, moves); + } + ArrayList strMoves = new ArrayList(); + for (Move m : moves) { + String mStr = TextIO.moveToString(pos, m, true); + strMoves.add(mStr); +// System.out.println(mStr); + } + return strMoves; + } +} diff --git a/DroidFishTest/src/org/petero/droidfish/gamelogic/MoveTest.java b/DroidFishTest/src/org/petero/droidfish/gamelogic/MoveTest.java new file mode 100644 index 0000000..22cbafc --- /dev/null +++ b/DroidFishTest/src/org/petero/droidfish/gamelogic/MoveTest.java @@ -0,0 +1,59 @@ +/* + DroidFish - An Android chess program. + Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package org.petero.droidfish.gamelogic; + +import junit.framework.TestCase; + +/** + * + * @author petero + */ +public class MoveTest extends TestCase { + + public MoveTest() { + } + + /** + * Test of move constructor, of class Move. + */ + public void testMoveConstructor() { + int f = Position.getSquare(4, 1); + int t = Position.getSquare(4, 3); + int p = Piece.WROOK; + Move move = new Move(f, t, p); + assertEquals(move.from, f); + assertEquals(move.to,t); + assertEquals(move.promoteTo, p); + } + + /** + * Test of equals, of class Move. + */ + public void testEquals() { + Move m1 = new Move(Position.getSquare(0, 6), Position.getSquare(1, 7), Piece.WROOK); + Move m2 = new Move(Position.getSquare(0, 6), Position.getSquare(0, 7), Piece.WROOK); + Move m3 = new Move(Position.getSquare(1, 6), Position.getSquare(1, 7), Piece.WROOK); + Move m4 = new Move(Position.getSquare(0, 6), Position.getSquare(1, 7), Piece.WKNIGHT); + Move m5 = new Move(Position.getSquare(0, 6), Position.getSquare(1, 7), Piece.WROOK); + assertTrue(!m1.equals(m2)); + assertTrue(!m1.equals(m3)); + assertTrue(!m1.equals(m4)); + assertTrue(m1.equals(m5)); + } +} diff --git a/DroidFishTest/src/org/petero/droidfish/gamelogic/PieceTest.java b/DroidFishTest/src/org/petero/droidfish/gamelogic/PieceTest.java new file mode 100644 index 0000000..380fa34 --- /dev/null +++ b/DroidFishTest/src/org/petero/droidfish/gamelogic/PieceTest.java @@ -0,0 +1,42 @@ +/* + DroidFish - An Android chess program. + Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package org.petero.droidfish.gamelogic; + + +import junit.framework.TestCase; + +/** + * + * @author petero + */ +public class PieceTest extends TestCase { + + public PieceTest() { + } + + /** + * Test of isWhite method, of class Piece. + */ + public void testIsWhite() { + assertEquals(false, Piece.isWhite(Piece.BBISHOP)); + assertEquals(true , Piece.isWhite(Piece.WBISHOP)); + assertEquals(true , Piece.isWhite(Piece.WKING)); + assertEquals(false, Piece.isWhite(Piece.BKING)); + } +} diff --git a/DroidFishTest/src/org/petero/droidfish/gamelogic/PositionTest.java b/DroidFishTest/src/org/petero/droidfish/gamelogic/PositionTest.java new file mode 100644 index 0000000..f0ef483 --- /dev/null +++ b/DroidFishTest/src/org/petero/droidfish/gamelogic/PositionTest.java @@ -0,0 +1,455 @@ +/* + DroidFish - An Android chess program. + Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package org.petero.droidfish.gamelogic; + +import java.util.ArrayList; +import java.util.List; + +import junit.framework.TestCase; + +/** + * + * @author petero + */ +public class PositionTest extends TestCase { + + public PositionTest() { + } + + /** + * Test of getPiece method, of class Position. + */ + public void testGetPiece() throws ChessParseError { + Position pos = new Position(); + int result = pos.getPiece(0); + assertEquals(result, Piece.EMPTY); + + pos = TextIO.readFEN(TextIO.startPosFEN); + result = pos.getPiece(0); + assertEquals(result, Piece.WROOK); + for (int x = 0; x < 8; x++) { + for (int y = 0; y < 2; y++) { + int p1 = pos.getPiece(Position.getSquare(x, y)); + int p2 = pos.getPiece(Position.getSquare(x, 7-y)); + int bwDiff = Piece.BPAWN - Piece.WPAWN; + assertEquals(p2, p1 + bwDiff); + } + } + } + + /** + * Test of getIndex method, of class Position. + */ + public void testGetIndex() { + for (int x = 0; x < 8; x++) { + for (int y = 0; y < 8; y++) { + int sq = Position.getSquare(x, y); + int x2 = Position.getX(sq); + int y2 = Position.getY(sq); + assertEquals(x, x2); + assertEquals(y, y2); + } + } + } + + /** + * Test of setPiece method, of class Position. + */ + public void testSetPiece() { + Position instance = new Position(); + assertEquals(Piece.EMPTY, instance.getPiece(Position.getSquare(0, 0))); + instance.setPiece(Position.getSquare(3, 4), Piece.WKING); + assertEquals(Piece.WKING, instance.getPiece(Position.getSquare(3, 4))); + } + + /** + * Test of makeMove method, of class Position. + */ + public void testMakeMove() throws ChessParseError { + Position pos = TextIO.readFEN(TextIO.startPosFEN); + Position origPos = new Position(pos); + assertTrue(pos.equals(origPos)); + Move move = new Move(Position.getSquare(4,1), Position.getSquare(4,3), Piece.EMPTY); + UndoInfo ui = new UndoInfo(); + pos.makeMove(move, ui); + assertEquals(pos.whiteMove, false); + assertEquals(-1, pos.getEpSquare()); + assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(4,1))); + assertEquals(Piece.WPAWN, pos.getPiece(Position.getSquare(4,3))); + assertTrue(!pos.equals(origPos)); + int castleMask = (1 << Position.A1_CASTLE) | + (1 << Position.H1_CASTLE) | + (1 << Position.A8_CASTLE) | + (1 << Position.H8_CASTLE); + assertEquals(castleMask,pos.getCastleMask()); + pos.unMakeMove(move, ui); + assertEquals(pos.whiteMove, true); + assertEquals(Piece.WPAWN, pos.getPiece(Position.getSquare(4,1))); + assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(4,3))); + assertTrue(pos.equals(origPos)); + + String fen = "r1bqk2r/2ppbppp/p1n2n2/1pP1p3/B3P3/5N2/PP1P1PPP/RNBQK2R w KQkq b6 0 2"; + pos = TextIO.readFEN(fen); + assertEquals(fen, TextIO.toFEN(pos)); + origPos = new Position(pos); + assertEquals(Position.getSquare(1,5), pos.getEpSquare()); + + // Test capture + move = new Move(Position.getSquare(0, 3), Position.getSquare(1,4), Piece.EMPTY); + pos.makeMove(move, ui); + assertEquals(-1, pos.getEpSquare()); + assertEquals(Piece.WBISHOP, pos.getPiece(Position.getSquare(1,4))); + assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(0,3))); + pos.unMakeMove(move, ui); + assertTrue(pos.equals(origPos)); + + // Test castling + move = new Move(Position.getSquare(4, 0), Position.getSquare(6,0), Piece.EMPTY); + pos.makeMove(move, ui); + assertEquals(Piece.WROOK, pos.getPiece(Position.getSquare(5,0))); + assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(7,0))); + castleMask = (1 << Position.A8_CASTLE) | + (1 << Position.H8_CASTLE); + assertEquals(castleMask,pos.getCastleMask()); + assertEquals(-1, pos.getEpSquare()); + pos.unMakeMove(move, ui); + assertTrue(pos.equals(origPos)); + + // Test castling rights (king move) + move = new Move(Position.getSquare(4, 0), Position.getSquare(4,1), Piece.EMPTY); + pos.makeMove(move, ui); + castleMask = (1 << Position.A8_CASTLE) | + (1 << Position.H8_CASTLE); + assertEquals(castleMask,pos.getCastleMask()); + assertEquals(-1, pos.getEpSquare()); + pos.unMakeMove(move, ui); + assertTrue(pos.equals(origPos)); + + // Test castling rights (rook move) + move = new Move(Position.getSquare(7, 0), Position.getSquare(6,0), Piece.EMPTY); + pos.makeMove(move, ui); + castleMask = (1 << Position.A1_CASTLE) | + (1 << Position.A8_CASTLE) | + (1 << Position.H8_CASTLE); + assertEquals(castleMask,pos.getCastleMask()); + assertEquals(-1, pos.getEpSquare()); + pos.unMakeMove(move, ui); + assertTrue(pos.equals(origPos)); + + // Test en passant + move = new Move(Position.getSquare(2, 4), Position.getSquare(1,5), Piece.EMPTY); + pos.makeMove(move, ui); + assertEquals(Piece.WPAWN, pos.getPiece(Position.getSquare(1,5))); + assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(2,4))); + assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(1,4))); + pos.unMakeMove(move, ui); + assertTrue(pos.equals(origPos)); + + // Test castling rights loss when rook captured + pos.setPiece(Position.getSquare(6,2), Piece.BKNIGHT); + pos.setWhiteMove(false); + Position origPos2 = new Position(pos); + move = new Move(Position.getSquare(6,2), Position.getSquare(7,0), Piece.EMPTY); + pos.makeMove(move, ui); + castleMask = (1 << Position.A1_CASTLE) | + (1 << Position.A8_CASTLE) | + (1 << Position.H8_CASTLE); + assertEquals(castleMask,pos.getCastleMask()); + assertEquals(-1, pos.getEpSquare()); + pos.unMakeMove(move, ui); + assertTrue(pos.equals(origPos2)); + } + + /** + * Test of makeMove method, of class Position. + */ + public void testPromotion() throws ChessParseError { + String fen = "r1bqk2r/1Pppbppp/p1n2n2/2P1p3/B3P3/5N2/Pp1P1PPP/R1BQK2R w KQkq - 0 1"; + Position pos = TextIO.readFEN(fen); + assertEquals(fen, TextIO.toFEN(pos)); + Position origPos = new Position(pos); + assertEquals(origPos, pos); + + Move move = new Move(Position.getSquare(1, 6), Position.getSquare(0,7), Piece.WQUEEN); + UndoInfo ui = new UndoInfo(); + pos.makeMove(move, ui); + assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(1,6))); + assertEquals(Piece.WQUEEN, pos.getPiece(Position.getSquare(0,7))); + pos.unMakeMove(move, ui); + assertEquals(origPos, pos); + + move = new Move(Position.getSquare(1, 6), Position.getSquare(1,7), Piece.WKNIGHT); + ui = new UndoInfo(); + pos.makeMove(move, ui); + assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(1,6))); + assertEquals(Piece.WKNIGHT, pos.getPiece(Position.getSquare(1,7))); + pos.unMakeMove(move, ui); + assertEquals(origPos, pos); + + pos.setWhiteMove(false); + origPos = new Position(pos); + + move = new Move(Position.getSquare(1, 1), Position.getSquare(2, 0), Piece.BROOK); + ui = new UndoInfo(); + pos.makeMove(move, ui); + assertEquals(Piece.EMPTY, pos.getPiece(Position.getSquare(1,1))); + assertEquals(Piece.BROOK, pos.getPiece(Position.getSquare(2,0))); + pos.unMakeMove(move, ui); + assertEquals(origPos, pos); + } + + /** + * Test move counters, of class Position. + */ + public void testMoveCounters() throws ChessParseError { + String fen = "r1bqk2r/2ppbppp/p1n2n2/1pP1p3/B3P3/5N2/PP1P1PPP/RNBQK2R w KQkq b6 0 7"; + Position pos = TextIO.readFEN(fen); + + Move move = TextIO.stringToMove(pos, "Nc3"); + UndoInfo ui = new UndoInfo(); + pos.makeMove(move, ui); + assertEquals(1, pos.halfMoveClock); + assertEquals(7, pos.fullMoveCounter); + pos.unMakeMove(move, ui); + + move = TextIO.stringToMove(pos, "O-O"); + pos.makeMove(move, ui); + assertEquals(1, pos.halfMoveClock); // Castling does not reset 50 move counter + assertEquals(7, pos.fullMoveCounter); + pos.unMakeMove(move, ui); + + move = TextIO.stringToMove(pos, "a3"); + pos.makeMove(move, ui); + assertEquals(0, pos.halfMoveClock); // Pawn move resets 50 move counter + assertEquals(7, pos.fullMoveCounter); + pos.unMakeMove(move, ui); + + move = TextIO.stringToMove(pos, "Nxe5"); + pos.makeMove(move, ui); + assertEquals(0, pos.halfMoveClock); // Capture move resets 50 move counter + assertEquals(7, pos.fullMoveCounter); + pos.unMakeMove(move, ui); + + move = TextIO.stringToMove(pos, "cxb6"); + pos.makeMove(move, ui); + assertEquals(0, pos.halfMoveClock); // EP capture move resets 50 move counter + assertEquals(7, pos.fullMoveCounter); + pos.unMakeMove(move, ui); + + move = TextIO.stringToMove(pos, "Kf1"); + pos.makeMove(move, ui); + assertEquals(1, pos.halfMoveClock); // Loss of castling rights does not reset 50 move counter + assertEquals(7, pos.fullMoveCounter); + pos.unMakeMove(move, ui); + + Move firstMove = TextIO.stringToMove(pos, "Nc3"); + UndoInfo firstUi = new UndoInfo(); + pos.makeMove(move, firstUi); + move = TextIO.stringToMove(pos, "O-O"); + pos.makeMove(move, ui); + assertEquals(2, pos.halfMoveClock); + assertEquals(8, pos.fullMoveCounter); // Black move increases fullMoveCounter + pos.unMakeMove(move, ui); + pos.unMakeMove(firstMove, firstUi); + + fen = "8/8/8/4k3/8/8/2p5/5K2 b - - 47 68"; + pos = TextIO.readFEN(fen); + move = TextIO.stringToMove(pos, "c1Q"); + pos.makeMove(move, ui); + assertEquals(0, pos.halfMoveClock); // Pawn promotion resets 50 move counter + assertEquals(69, pos.fullMoveCounter); + } + + /** + * Test of drawRuleEquals, of class Position. + */ + public void testDrawRuleEquals() throws ChessParseError { + Position pos = TextIO.readFEN(TextIO.startPosFEN); + Position origPos = new Position(pos); + UndoInfo ui = new UndoInfo(); + pos.makeMove(TextIO.stringToMove(pos, "Nf3"), ui); + assertEquals(false, pos.drawRuleEquals(origPos)); + pos.makeMove(TextIO.stringToMove(pos, "Nf6"), ui); + assertEquals(false, pos.drawRuleEquals(origPos)); + pos.makeMove(TextIO.stringToMove(pos, "Ng1"), ui); + assertEquals(false, pos.drawRuleEquals(origPos)); + pos.makeMove(TextIO.stringToMove(pos, "Ng8"), ui); + assertEquals(true, pos.drawRuleEquals(origPos)); + assertEquals(false, pos.equals(origPos)); // Move counters have changed + + String fen = "r1bqkb1r/pppp1ppp/2n2n2/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w KQkq - 0 1"; + pos = TextIO.readFEN(fen); + origPos = new Position(pos); + pos.makeMove(TextIO.stringToMove(pos, "Ke2"), ui); + assertEquals(false, pos.drawRuleEquals(origPos)); + pos.makeMove(TextIO.stringToMove(pos, "Be7"), ui); + assertEquals(false, pos.drawRuleEquals(origPos)); + pos.makeMove(TextIO.stringToMove(pos, "Ke1"), ui); + assertEquals(false, pos.drawRuleEquals(origPos)); + pos.makeMove(TextIO.stringToMove(pos, "Bf8"), ui); + assertEquals(false, pos.drawRuleEquals(origPos)); // Not equal, castling rights lost + + pos = TextIO.readFEN(TextIO.startPosFEN); + pos.makeMove(TextIO.stringToMove(pos, "c4"), ui); + pos.makeMove(TextIO.stringToMove(pos, "a6"), ui); + pos.makeMove(TextIO.stringToMove(pos, "c5"), ui); + pos.makeMove(TextIO.stringToMove(pos, "b5"), ui); + assertEquals(Position.getSquare(1, 5), pos.getEpSquare()); + origPos = new Position(pos); + pos.makeMove(TextIO.stringToMove(pos, "Nc3"), ui); + pos.makeMove(TextIO.stringToMove(pos, "Nc6"), ui); + pos.makeMove(TextIO.stringToMove(pos, "Nb1"), ui); + pos.makeMove(TextIO.stringToMove(pos, "Nb8"), ui); + assertEquals(false, pos.drawRuleEquals(origPos)); // Not equal, en passant rights lost + } + + /** + * Test of hashCode method, of class Position. + */ + public void testHashCode() throws ChessParseError { + Position pos = TextIO.readFEN(TextIO.startPosFEN); + long h1 = pos.zobristHash(); + assertEquals(h1, pos.computeZobristHash()); + UndoInfo ui = new UndoInfo(); + Move move = TextIO.stringToMove(pos, "e4"); + pos.makeMove(move, ui); + assertTrue(h1 != pos.zobristHash()); + pos.unMakeMove(move, ui); + assertTrue(h1 == pos.zobristHash()); + + pos.setWhiteMove(!pos.whiteMove); + long h4 = pos.zobristHash(); + assertEquals(h4, pos.computeZobristHash()); + assertTrue(h1 != pos.zobristHash()); + pos.setWhiteMove(!pos.whiteMove); + assertTrue(h1 == pos.zobristHash()); + + pos.setCastleMask(0); + assertTrue(h1 != pos.zobristHash()); + + pos = TextIO.readFEN("rnbqkbnr/pppp1ppp/8/2P1p3/8/8/PP1PPPPP/RNBQKBNR b KQkq - 0 1"); + h1 = pos.zobristHash(); + assertEquals(h1, pos.computeZobristHash()); + + String[] moves = { + "b5", "Nc3", "Nf6", "Nb1", "Ng8", "Nc3", "Nf6", "Nb1", "Ng8", "Nc3", "d5", + "cxd6", "Qxd6", "h4", "Be6", "h5", "Nc6", "h6", "o-o-o", "hxg7", "Nf6", "gxh8Q", "Be7" + }; + List uiList = new ArrayList(); + List hashList = new ArrayList(); + List moveList = new ArrayList(); + for (int i = 0; i < moves.length; i++) { + uiList.add(new UndoInfo()); + Move m = TextIO.stringToMove(pos, moves[i]); + moveList.add(m); + pos.makeMove(m, uiList.get(i)); + long h = pos.zobristHash(); + assertEquals(h, pos.computeZobristHash()); + hashList.add(h); + } + assertTrue(!hashList.get(0).equals(hashList.get(4))); + assertTrue(hashList.get(4).equals(hashList.get(8))); + for (int i = moves.length - 1; i >= 0; i--) { + pos.unMakeMove(moveList.get(i), uiList.get(i)); + long h = pos.zobristHash(); + assertEquals(h, pos.computeZobristHash()); + assertEquals(h, i > 0 ? hashList.get(i - 1) : h1); + } + } + + /** + * Test of getKingSq method, of class Position. + */ + public void testGetKingSq() throws ChessParseError { + Position pos = TextIO.readFEN(TextIO.startPosFEN); + assertEquals(TextIO.getSquare("e1"), pos.getKingSq(true)); + assertEquals(TextIO.getSquare("e8"), pos.getKingSq(false)); + pos = TextIO.readFEN("r1bq1bnr/ppppkppp/2n5/1B2p3/4P3/5N2/PPPP1PPP/RNBQK2R w KQ - 0 4"); + assertEquals(TextIO.getSquare("e1"), pos.getKingSq(true)); + assertEquals(TextIO.getSquare("e7"), pos.getKingSq(false)); + UndoInfo ui = new UndoInfo(); + pos.makeMove(TextIO.stringToMove(pos, "o-o"), ui); + assertEquals(TextIO.getSquare("g1"), pos.getKingSq(true)); + assertEquals(TextIO.getSquare("e7"), pos.getKingSq(false)); + pos.makeMove(TextIO.stringToMove(pos, "Kd6"), ui); + assertEquals(TextIO.getSquare("g1"), pos.getKingSq(true)); + assertEquals(TextIO.getSquare("d6"), pos.getKingSq(false)); + } + + public void testNullMove() throws ChessParseError { + Move nullMove = new Move(0, 0, 0); + UndoInfo ui = new UndoInfo(); + + Position pos = TextIO.readFEN(TextIO.startPosFEN); + Position origPos = new Position(pos); + pos.makeMove(nullMove, ui); + assertEquals(false, pos.whiteMove); + assertEquals(true, pos.a1Castle()); + assertEquals(Piece.WROOK, pos.getPiece(0)); + pos.unMakeMove(nullMove, ui); + assertTrue(pos.equals(origPos)); + + pos = TextIO.readFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w Kkq - 5 1"); + origPos = new Position(pos); + pos.makeMove(nullMove, ui); + assertEquals(false, pos.whiteMove); + assertEquals(false, pos.a1Castle()); + assertEquals(Piece.WROOK, pos.getPiece(0)); + assertEquals(0, pos.halfMoveClock); + pos.unMakeMove(nullMove, ui); + assertTrue(pos.equals(origPos)); + + pos = TextIO.readFEN("7k/8/8/8/8/8/4P3/K7 w - - 5 1"); + origPos = new Position(pos); + pos.makeMove(nullMove, ui); + assertEquals(false, pos.whiteMove); + assertEquals(false, pos.a1Castle()); + assertEquals(0, pos.halfMoveClock); + assertEquals(0, pos.getKingSq(true)); + assertEquals(63, pos.getKingSq(false)); + + UndoInfo ui2 = new UndoInfo(); + pos.makeMove(nullMove, ui2); + assertEquals(true, pos.whiteMove); + assertEquals(false, pos.a1Castle()); + assertEquals(0, pos.halfMoveClock); + assertEquals(0, pos.getKingSq(true)); + assertEquals(63, pos.getKingSq(false)); + + pos.unMakeMove(nullMove, ui2); + assertEquals(false, pos.whiteMove); + assertEquals(false, pos.a1Castle()); + assertEquals(0, pos.halfMoveClock); + assertEquals(0, pos.getKingSq(true)); + assertEquals(63, pos.getKingSq(false)); + + pos.unMakeMove(nullMove, ui); + assertTrue(pos.equals(origPos)); + + pos = TextIO.readFEN("5k2/8/4R1K1/8/8/8/8/8 b - - 5 1"); + origPos = new Position(pos); + pos.makeMove(nullMove, ui); + assertEquals(true, pos.whiteMove); + assertEquals(false, pos.a1Castle()); + assertEquals(0, pos.halfMoveClock); + + pos.unMakeMove(nullMove, ui); + assertTrue(pos.equals(origPos)); + } +} diff --git a/DroidFishTest/src/org/petero/droidfish/gamelogic/TextIOTest.java b/DroidFishTest/src/org/petero/droidfish/gamelogic/TextIOTest.java new file mode 100644 index 0000000..77622b5 --- /dev/null +++ b/DroidFishTest/src/org/petero/droidfish/gamelogic/TextIOTest.java @@ -0,0 +1,382 @@ +/* + DroidFish - An Android chess program. + Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package org.petero.droidfish.gamelogic; + + +import junit.framework.TestCase; + +/** + * + * @author petero + */ +public class TextIOTest extends TestCase { + + public TextIOTest() { + } + + /** + * Test of readFEN method, of class TextIO. + */ + public void testReadFEN() throws ChessParseError { + String fen = "rnbqk2r/1p3ppp/p7/1NpPp3/QPP1P1n1/P4N2/4KbPP/R1B2B1R b kq - 0 1"; + Position pos = TextIO.readFEN(fen); + assertEquals(fen, TextIO.toFEN(pos)); + assertEquals(pos.getPiece(Position.getSquare(0, 3)), Piece.WQUEEN); + assertEquals(pos.getPiece(Position.getSquare(4, 7)), Piece.BKING); + assertEquals(pos.getPiece(Position.getSquare(4, 1)), Piece.WKING); + assertEquals(pos.whiteMove, false); + assertEquals(pos.a1Castle(), false); + assertEquals(pos.h1Castle(), false); + assertEquals(pos.a8Castle(), true); + assertEquals(pos.h8Castle(), true); + + fen = "8/3k4/8/5pP1/1P6/1NB5/2QP4/R3K2R w KQ f6 1 2"; + pos = TextIO.readFEN(fen); + assertEquals(fen, TextIO.toFEN(pos)); + assertEquals(1, pos.halfMoveClock); + assertEquals(2, pos.fullMoveCounter); + + // Must have exactly one king + boolean wasError = testFENParseError("8/8/8/8/8/8/8/kk1K4 w - - 0 1"); + assertEquals(true, wasError); + + // Must not be possible to capture the king + wasError = testFENParseError("8/8/8/8/8/8/8/k1RK4 w - - 0 1"); + assertEquals(true, wasError); + + // Make sure bogus en passant square information is removed + fen = "rnbqkbnr/pppppppp/8/8/4P3/8/PPPP1PPP/RNBQKBNR b KQkq e3 0 1"; + pos = TextIO.readFEN(fen); + assertEquals(-1, pos.getEpSquare()); + + // Test for too many rows (slashes) + wasError = testFENParseError("8/8/8/8/4k3/8/8/8/KBN5 w - - 0 1"); + assertEquals(true, wasError); + + // Test for too many columns + wasError = testFENParseError("8K/8/8/8/4k3/8/8/8 w - - 0 1"); + assertEquals(true, wasError); + + // Pawns must not be on first/last rank + wasError = testFENParseError("kp6/8/8/8/8/8/8/K7 w - - 0 1"); + assertEquals(true, wasError); + + wasError = testFENParseError("kr/pppp/8/8/8/8/8/KBR w"); + assertEquals(false, wasError); // OK not to specify castling flags and ep square + + wasError = testFENParseError("k/8/8/8/8/8/8/K"); + assertEquals(true, wasError); // Error side to move not specified + + wasError = testFENParseError(""); + assertEquals(true, wasError); + + wasError = testFENParseError(" |"); + assertEquals(true, wasError); + + wasError = testFENParseError("1B1B4/6k1/7r/7P/6q1/r7/q7/7K b - - acn 6; acs 0;"); + assertEquals(false, wasError); // Extra stuff after FEN string is allowed + + pos = TextIO.readFEN("3r2k1/p4p1p/1ppq2p1/8/2PpQ3/1P5P/P4PP1/3R1K2 b - - 1 26\n"); + assertEquals(26, pos.fullMoveCounter); + + // Test that insane move numbers are rejected. Otherwise, could cause problems + // (excessive memory usage) for the clock history class. + pos = TextIO.readFEN("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 10000"); + assertEquals(10000, pos.fullMoveCounter); + } + + /** + * Test that readFEN removes bogus castle flags. + */ + public void testReadFENCastleFlags() throws ChessParseError { + String fenBogus = "rnbqk2r/1p3ppp/p7/1NpPp3/QPP1P1n1/P4N2/4KbPP/R1B2B1R w KQkq - 0 1"; + Position pos = TextIO.readFEN(fenBogus); + String fenCorrect = "rnbqk2r/1p3ppp/p7/1NpPp3/QPP1P1n1/P4N2/4KbPP/R1B2B1R w kq - 0 1"; + assertEquals(fenCorrect, TextIO.toFEN(pos)); + } + + /** Tests if trying to parse a FEN string causes an error. */ + private boolean testFENParseError(String fen) { + boolean wasError; + wasError = false; + try { + TextIO.readFEN(fen); + } catch (ChessParseError err) { + wasError = true; + } + return wasError; + } + + /** + * Test of moveToString method, of class TextIO. + */ + public void testMoveToString() throws ChessParseError { + Position pos = TextIO.readFEN(TextIO.startPosFEN); + assertEquals(TextIO.startPosFEN, TextIO.toFEN(pos)); + Move move = new Move(Position.getSquare(4, 1), Position.getSquare(4, 3), + Piece.EMPTY); + boolean longForm = true; + String result = TextIO.moveToString(pos, move, longForm); + assertEquals("e2-e4", result); + + move = new Move(Position.getSquare(6, 0), Position.getSquare(5, 2), Piece.EMPTY); + result = TextIO.moveToString(pos, move, longForm); + assertEquals("Ng1-f3", result); + + move = new Move(Position.getSquare(4, 7), Position.getSquare(2, 7), + Piece.EMPTY); + result = TextIO.moveToString(pos, move, longForm); + assertEquals("O-O-O", result); + + String fen = "1r3k2/2P5/8/8/8/4K3/8/8 w - - 0 1"; + pos = TextIO.readFEN(fen); + assertEquals(fen, TextIO.toFEN(pos)); + move = new Move(Position.getSquare(2,6), Position.getSquare(1,7), Piece.WROOK); + result = TextIO.moveToString(pos, move, longForm); + assertEquals("c7xb8R+", result); + + move = new Move(Position.getSquare(2,6), Position.getSquare(2,7), Piece.WKNIGHT); + result = TextIO.moveToString(pos, move, longForm); + assertEquals("c7-c8N", result); + + move = new Move(Position.getSquare(2,6), Position.getSquare(2,7), Piece.WQUEEN); + result = TextIO.moveToString(pos, move, longForm); + assertEquals("c7-c8Q+", result); + + // Test null move + pos = TextIO.readFEN(TextIO.startPosFEN); + Move nullMove = new Move(0, 0, 0); + result = TextIO.moveToString(pos, nullMove, false); + assertEquals("--", result); + result = TextIO.moveToString(pos, nullMove, true); + assertEquals("--", result); + } + + /** + * Test of moveToString method, of class TextIO, mate/stalemate tests. + */ + public void testMoveToStringMate() throws ChessParseError { + Position pos = TextIO.readFEN("3k4/1PR5/3N4/8/4K3/8/8/8 w - - 0 1"); + boolean longForm = true; + + Move move = new Move(Position.getSquare(1, 6), Position.getSquare(1, 7), Piece.WROOK); + String result = TextIO.moveToString(pos, move, longForm); + assertEquals("b7-b8R+", result); // check + + move = new Move(Position.getSquare(1, 6), Position.getSquare(1, 7), Piece.WQUEEN); + result = TextIO.moveToString(pos, move, longForm); + assertEquals("b7-b8Q#", result); // check mate + + move = new Move(Position.getSquare(1, 6), Position.getSquare(1, 7), Piece.WKNIGHT); + result = TextIO.moveToString(pos, move, longForm); + assertEquals("b7-b8N", result); + + move = new Move(Position.getSquare(1, 6), Position.getSquare(1, 7), Piece.WBISHOP); + result = TextIO.moveToString(pos, move, longForm); + assertEquals("b7-b8B", result); // stalemate + } + + /** + * Test of moveToString method, of class TextIO, short form. + */ + public void testMoveToStringShortForm() throws ChessParseError { + String fen = "r4rk1/2pn3p/2q1q1n1/8/2q2p2/6R1/p4PPP/1R4K1 b - - 0 1"; + Position pos = TextIO.readFEN(fen); + assertEquals(fen, TextIO.toFEN(pos)); + boolean longForm = false; + + Move move = new Move(Position.getSquare(4,5), Position.getSquare(4,3), Piece.EMPTY); + String result = TextIO.moveToString(pos, move, longForm); + assertEquals("Qee4", result); // File disambiguation needed + + move = new Move(Position.getSquare(2,5), Position.getSquare(4,3), Piece.EMPTY); + result = TextIO.moveToString(pos, move, longForm); + assertEquals("Qc6e4", result); // Full disambiguation needed + + move = new Move(Position.getSquare(2,3), Position.getSquare(4,3), Piece.EMPTY); + result = TextIO.moveToString(pos, move, longForm); + assertEquals("Q4e4", result); // Row disambiguation needed + + move = new Move(Position.getSquare(2,3), Position.getSquare(2,0), Piece.EMPTY); + result = TextIO.moveToString(pos, move, longForm); + assertEquals("Qc1+", result); // No disambiguation needed + + move = new Move(Position.getSquare(0,1), Position.getSquare(0,0), Piece.BQUEEN); + result = TextIO.moveToString(pos, move, longForm); + assertEquals("a1Q", result); // Normal promotion + + move = new Move(Position.getSquare(0,1), Position.getSquare(1,0), Piece.BQUEEN); + result = TextIO.moveToString(pos, move, longForm); + assertEquals("axb1Q#", result); // Capture promotion and check mate + + move = new Move(Position.getSquare(0,1), Position.getSquare(1,0), Piece.BKNIGHT); + result = TextIO.moveToString(pos, move, longForm); + assertEquals("axb1N", result); // Capture promotion + + move = new Move(Position.getSquare(3,6), Position.getSquare(4,4), Piece.EMPTY); + result = TextIO.moveToString(pos, move, longForm); + assertEquals("Ne5", result); // Other knight pinned, no disambiguation needed + + move = new Move(Position.getSquare(7,6), Position.getSquare(7,4), Piece.EMPTY); + result = TextIO.moveToString(pos, move, longForm); + assertEquals("h5", result); // Regular pawn move + + move = new Move(Position.getSquare(5,7), Position.getSquare(3,7), Piece.EMPTY); + result = TextIO.moveToString(pos, move, longForm); + assertEquals("Rfd8", result); // File disambiguation needed + } + + /** + * Test of stringToMove method, of class TextIO. + */ + public void testStringToMove() throws ChessParseError { + Position pos = TextIO.readFEN("r4rk1/2pn3p/2q1q1n1/8/2q2p2/6R1/p4PPP/1R4K1 b - - 0 1"); + + Move mNe5 = new Move(Position.getSquare(3, 6), Position.getSquare(4, 4), Piece.EMPTY); + Move m = TextIO.stringToMove(pos, "Ne5"); + assertEquals(mNe5, m); + m = TextIO.stringToMove(pos, "ne"); + assertEquals(mNe5, m); + m = TextIO.stringToMove(pos, "N"); + assertEquals(null, m); + + Move mQc6e4 = new Move(Position.getSquare(2, 5), Position.getSquare(4, 3), Piece.EMPTY); + m = TextIO.stringToMove(pos, "Qc6-e4"); + assertEquals(mQc6e4, m); + m = TextIO.stringToMove(pos, "Qc6e4"); + assertEquals(mQc6e4, m); + m = TextIO.stringToMove(pos, "Qce4"); + assertEquals(null, m); + m = TextIO.stringToMove(pos, "Q6e4"); + assertEquals(null, m); + + Move maxb1Q = new Move(Position.getSquare(0, 1), Position.getSquare(1, 0), Piece.BQUEEN); + m = TextIO.stringToMove(pos, "axb1Q"); + assertEquals(maxb1Q, m); + m = TextIO.stringToMove(pos, "axb1Q#"); + assertEquals(maxb1Q, m); + m = TextIO.stringToMove(pos, "axb1Q+"); + assertEquals(maxb1Q, m); + + Move mh5= new Move(Position.getSquare(7, 6), Position.getSquare(7, 4), Piece.EMPTY); + m = TextIO.stringToMove(pos, "h5"); + assertEquals(mh5, m); + m = TextIO.stringToMove(pos, "h7-h5"); + assertEquals(mh5, m); + m = TextIO.stringToMove(pos, "h"); + assertEquals(null, m); + + pos = TextIO.readFEN("r1b1k2r/1pqpppbp/p5pn/3BP3/8/2pP4/PPPBQPPP/R3K2R w KQkq - 0 12"); + m = TextIO.stringToMove(pos, "bxc3"); + assertEquals(TextIO.getSquare("b2"), m.from); + m = TextIO.stringToMove(pos, "Bxc3"); + assertEquals(TextIO.getSquare("d2"), m.from); + m = TextIO.stringToMove(pos, "bxc"); + assertEquals(TextIO.getSquare("b2"), m.from); + m = TextIO.stringToMove(pos, "Bxc"); + assertEquals(TextIO.getSquare("d2"), m.from); + + // Test castling. o-o is a substring of o-o-o, which could cause problems. + pos = TextIO.readFEN("5k2/p1pQn3/1p2Bp1r/8/4P1pN/2N5/PPP2PPP/R3K2R w KQ - 0 16"); + Move kCastle = new Move(Position.getSquare(4,0), Position.getSquare(6,0), Piece.EMPTY); + Move qCastle = new Move(Position.getSquare(4,0), Position.getSquare(2,0), Piece.EMPTY); + m = TextIO.stringToMove(pos, "o"); + assertEquals(null, m); + m = TextIO.stringToMove(pos, "o-o"); + assertEquals(kCastle, m); + m = TextIO.stringToMove(pos, "O-O"); + assertEquals(kCastle, m); + m = TextIO.stringToMove(pos, "0-0"); + assertEquals(kCastle, m); + m = TextIO.stringToMove(pos, "O-O-O"); + assertEquals(qCastle, m); + m = TextIO.stringToMove(pos, "o-o-o"); + assertEquals(qCastle, m); + m = TextIO.stringToMove(pos, "0-0-0"); + assertEquals(qCastle, m); + + // Test 'o-o+' + pos.setPiece(Position.getSquare(5,1), Piece.EMPTY); + pos.setPiece(Position.getSquare(5,5), Piece.EMPTY); + m = TextIO.stringToMove(pos, "o"); + assertEquals(null, m); + m = TextIO.stringToMove(pos, "o-o"); + assertEquals(kCastle, m); + m = TextIO.stringToMove(pos, "o-o-o"); + assertEquals(qCastle, m); + m = TextIO.stringToMove(pos, "o-o+"); + assertEquals(kCastle, m); + + // Test d8=Q+ syntax + pos = TextIO.readFEN("1r3r2/2kP2Rp/p1bN1p2/2p5/5P2/2P5/P5PP/3R2K1 w - -"); + m = TextIO.stringToMove(pos, "d8=Q+"); + Move m2 = TextIO.stringToMove(pos, "d8Q"); + assertEquals(m2, m); + + // Test null move + pos = TextIO.readFEN(TextIO.startPosFEN); + Move nullMove = new Move(0, 0, 0); + m = TextIO.stringToMove(pos, "--"); + assertEquals(nullMove, m); + + // Test extra characters + pos = TextIO.readFEN(TextIO.startPosFEN); + Move mNf3 = new Move(TextIO.getSquare("g1"), TextIO.getSquare("f3"), Piece.EMPTY); + assertEquals(mNf3, TextIO.stringToMove(pos, "Ngf3")); + assertEquals(mNf3, TextIO.stringToMove(pos, "Ng1f3")); + assertEquals(mNf3, TextIO.stringToMove(pos, "Ng1-f3")); + assertEquals(mNf3, TextIO.stringToMove(pos, "g1f3")); + assertEquals(mNf3, TextIO.stringToMove(pos, "N1f3")); + assertEquals(mNf3, TextIO.stringToMove(pos, "Ngf")); + assertEquals(mNf3, TextIO.stringToMove(pos, "Nf")); + } + + /** + * Test of getSquare method, of class TextIO. + */ + public void testGetSquare() throws ChessParseError { + assertEquals(Position.getSquare(0, 0), TextIO.getSquare("a1")); + assertEquals(Position.getSquare(1, 7), TextIO.getSquare("b8")); + assertEquals(Position.getSquare(3, 3), TextIO.getSquare("d4")); + assertEquals(Position.getSquare(4, 3), TextIO.getSquare("e4")); + assertEquals(Position.getSquare(3, 1), TextIO.getSquare("d2")); + assertEquals(Position.getSquare(7, 7), TextIO.getSquare("h8")); + } + + /** + * Test of squareToString method, of class TextIO. + */ + public void testSquareToString() { + assertEquals("a1", TextIO.squareToString(Position.getSquare(0, 0))); + assertEquals("h6", TextIO.squareToString(Position.getSquare(7, 5))); + assertEquals("e4", TextIO.squareToString(Position.getSquare(4, 3))); + } + + /** + * Test of asciiBoard method, of class TextIO. + */ + public void testAsciiBoard() throws ChessParseError { + Position pos = TextIO.readFEN("r4rk1/2pn3p/2q1q1n1/8/2q2p2/6R1/p4PPP/1R4K1 b - - 0 1"); + String aBrd = TextIO.asciiBoard(pos); +// System.out.print(aBrd); + assertEquals(12, aBrd.length() - aBrd.replaceAll("\\*", "").length()); // 12 black pieces + assertEquals(3, aBrd.length() - aBrd.replaceAll("\\*Q", " ").length()); // 3 black queens + assertEquals(3, aBrd.length() - aBrd.replaceAll(" P", " ").length()); // 3 white pawns + } +} diff --git a/DroidFishTest/src/org/petero/droidfish/gamelogic/TimeControlTest.java b/DroidFishTest/src/org/petero/droidfish/gamelogic/TimeControlTest.java new file mode 100644 index 0000000..fab2c24 --- /dev/null +++ b/DroidFishTest/src/org/petero/droidfish/gamelogic/TimeControlTest.java @@ -0,0 +1,136 @@ +/* + DroidFish - An Android chess program. + Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +package org.petero.droidfish.gamelogic; + +import junit.framework.TestCase; + + +public class TimeControlTest extends TestCase { + public TimeControlTest() { + } + + public void testElapsedTime() { + TimeControl tc = new TimeControl(); + long totTime = 5 * 60 * 1000; + long t0 = 1000; + tc.setTimeControl(totTime, 0, 0); + tc.setCurrentMove(1, true, totTime, totTime); + assertEquals(0, tc.getMovesToTC()); + assertEquals(0, tc.getIncrement()); + assertEquals(totTime, tc.getRemainingTime(true, 0)); + tc.startTimer(t0); + int remain = tc.moveMade(t0 + 1000, true); + assertEquals(totTime - 1000, remain); + + tc.setCurrentMove(2, true, totTime - 1000, totTime); + assertEquals(0, tc.getMovesToTC()); + assertEquals(totTime - 1000, tc.getRemainingTime(true, t0 + 4711)); + assertEquals(totTime, tc.getRemainingTime(false, t0 + 4711)); + + tc.setCurrentMove(1, false, totTime - 1000, totTime); + assertEquals(0, tc.getMovesToTC()); + assertEquals(totTime - 1000, tc.getRemainingTime(true, t0 + 4711)); + assertEquals(totTime, tc.getRemainingTime(false, t0 + 4711)); + + tc.startTimer(t0 + 3000); + assertEquals(totTime - 1000, tc.getRemainingTime(true, t0 + 5000)); + assertEquals(totTime - 2000, tc.getRemainingTime(false, t0 + 5000)); + tc.stopTimer(t0 + 8000); + assertEquals(totTime - 1000, tc.getRemainingTime(true, t0 + 4711)); + assertEquals(totTime - 5000, tc.getRemainingTime(false, t0 + 4711)); + remain = tc.moveMade(t0 + 8000, true); + assertEquals(totTime - 5000, remain); + tc.setCurrentMove(2, true, totTime - 1000, totTime - 5000); + assertEquals(totTime - 1000, tc.getRemainingTime(true, t0 + 4711)); + assertEquals(totTime - 5000, tc.getRemainingTime(false, t0 + 4711)); + } + + /** Test getMovesToTC */ + public void testTimeControl() { + TimeControl tc = new TimeControl(); + tc.setTimeControl(2 * 60 * 1000, 40, 0); + tc.setCurrentMove(1, true, 0, 0); + assertEquals(40, tc.getMovesToTC()); + tc.setCurrentMove(1, false, 0, 0); + assertEquals(40, tc.getMovesToTC()); + + tc.setCurrentMove(2, true, 0, 0); + assertEquals(39, tc.getMovesToTC()); + + tc.setCurrentMove(40, true, 0, 0); + assertEquals(1, tc.getMovesToTC()); + + tc.setCurrentMove(41, true, 0, 0); + assertEquals(40, tc.getMovesToTC()); + + tc.setCurrentMove(80, true, 0, 0); + assertEquals(1, tc.getMovesToTC()); + + tc.setCurrentMove(81, true, 0, 0); + assertEquals(40, tc.getMovesToTC()); + } + + public void testExtraTime() { + TimeControl tc = new TimeControl(); + final long timeCont = 60 * 1000; + int wBaseTime = (int)timeCont; + int bBaseTime = (int)timeCont; + final long inc = 700; + tc.setTimeControl(timeCont, 5, inc); + tc.setCurrentMove(5, true, wBaseTime, bBaseTime); + long t0 = 1342134; + assertEquals(timeCont, tc.getRemainingTime(true, t0 + 4711)); + assertEquals(timeCont, tc.getRemainingTime(false, t0 + 4711)); + + tc.startTimer(t0 + 1000); + wBaseTime = tc.moveMade(t0 + 2000, true); + tc.setCurrentMove(5, false, wBaseTime, bBaseTime); + assertEquals(timeCont - 1000 + timeCont + inc, tc.getRemainingTime(true, t0 + 4711)); + assertEquals(timeCont, tc.getRemainingTime(false, t0 + 4711)); + + tc.startTimer(t0 + 2000); + bBaseTime = tc.moveMade(t0 + 6000, true); + tc.setCurrentMove(6, true, wBaseTime, bBaseTime); + assertEquals(timeCont - 1000 + timeCont + inc, tc.getRemainingTime(true, t0 + 4711)); + assertEquals(timeCont - 4000 + timeCont + inc, tc.getRemainingTime(false, t0 + 4711)); + + tc.startTimer(t0 + 6000); + wBaseTime = tc.moveMade(t0 + 9000, true); + tc.setCurrentMove(6, false, wBaseTime, bBaseTime); + assertEquals(timeCont - 1000 + timeCont + inc - 3000 + inc, tc.getRemainingTime(true, t0 + 4711)); + assertEquals(timeCont - 4000 + timeCont + inc, tc.getRemainingTime(false, t0 + 4711)); + + // No increment when move made int paused mode, ie analysis mode + tc.startTimer(t0 + 9000); + bBaseTime = tc.moveMade(t0 + 10000, false); + tc.setCurrentMove(7, true, wBaseTime, bBaseTime); + assertEquals(timeCont - 1000 + timeCont + inc - 3000 + inc, tc.getRemainingTime(true, t0 + 4711)); + assertEquals(timeCont - 4000 + timeCont + inc - 1000, tc.getRemainingTime(false, t0 + 4711)); + + // No extra time when passing time control in analysis mode + tc.setTimeControl(timeCont, 1, inc); + wBaseTime = bBaseTime = (int)timeCont; + tc.setCurrentMove(1, true, wBaseTime, bBaseTime); + tc.startTimer(t0 + 1000); + wBaseTime = tc.moveMade(t0 + 3000, false); + tc.setCurrentMove(1, false, wBaseTime, bBaseTime); + assertEquals(timeCont - 2000 + (timeCont + inc)*0, tc.getRemainingTime(true, t0 + 4711)); + assertEquals(timeCont, tc.getRemainingTime(false, t0 + 4711)); + } +}