diff options
author | Jason Katz-Brown <jason@airbnb.com> | 2013-08-25 02:17:13 -0700 |
---|---|---|
committer | Jason Katz-Brown <jason@airbnb.com> | 2013-08-25 02:17:13 -0700 |
commit | 9306cb60c32082c5403931de0823a9fd5daa196c (patch) | |
tree | ca1b6eb695fdf3f0c2294e92416b272164bae642 /test | |
parent | 8fb2c681cecc01b46b0f4ba02d5cc177c4747b1c (diff) |
Initial git commit.
Diffstat (limited to 'test')
-rw-r--r-- | test/.gitignore | 7 | ||||
-rw-r--r-- | test/positions/boys1.gcg | 28 | ||||
-rw-r--r-- | test/positions/boys2.gcg | 31 | ||||
-rw-r--r-- | test/positions/boys3.gcg | 30 | ||||
-rw-r--r-- | test/positions/boys4.gcg | 28 | ||||
-rw-r--r-- | test/positions/boys5.gcg | 28 | ||||
-rw-r--r-- | test/positions/consonant_heaviness_tendency.gcg | 20 | ||||
-rw-r--r-- | test/positions/deadwoodendgame.gcg | 26 | ||||
-rw-r--r-- | test/positions/fivepoint.gcg | 32 | ||||
-rw-r--r-- | test/positions/logan.gcg | 28 | ||||
-rw-r--r-- | test/positions/logan_we.gcg | 25 | ||||
-rw-r--r-- | test/positions/multiplayer-crash.gcg | 22 | ||||
-rw-r--r-- | test/positions/one_tile_play.gcg | 5 | ||||
-rw-r--r-- | test/positions/preendgame.gcg | 23 | ||||
-rw-r--r-- | test/positions/preendgame2.gcg | 24 | ||||
-rw-r--r-- | test/positions/preendgame4.gcg | 23 | ||||
-rw-r--r-- | test/positions/short_game_with_bad_moves.gcg | 9 | ||||
-rw-r--r-- | test/test.pro | 32 | ||||
-rw-r--r-- | test/testharness.cpp | 796 | ||||
-rw-r--r-- | test/testharness.h | 117 | ||||
-rw-r--r-- | test/testmain.cpp | 39 | ||||
-rw-r--r-- | test/trademarkedboards.cpp | 148 | ||||
-rw-r--r-- | test/trademarkedboards.h | 40 |
23 files changed, 1561 insertions, 0 deletions
diff --git a/test/.gitignore b/test/.gitignore new file mode 100644 index 0000000..f42b7af --- /dev/null +++ b/test/.gitignore @@ -0,0 +1,7 @@ +Makefile +test +obj +Makefile.Debug +Makefile.Release +debug +release diff --git a/test/positions/boys1.gcg b/test/positions/boys1.gcg new file mode 100644 index 0000000..2a6f8db --- /dev/null +++ b/test/positions/boys1.gcg @@ -0,0 +1,28 @@ +#player1 Quackle Quackle Computer +#player2 David David Boys +#description Quackle Computer plays David Boys in Round 1 at the 2006 Human vs. Computer Showdown +#title 2006 Human vs. Computer Showdown Round 1 +#incomplete +>Quackle: DEMJNOT 8d JETON +40 40 +>David: ?EDYEIG h2 rEDYEING +64 64 +>Quackle: BEDGMNP 7e BEDIM +26 66 BE, ET, DO +>David: HEALERS j1 HEALERS +75 139 BEDIMS +>Quackle: DFGINPS k3 DIF +29 95 AD, LI, EF +>David: COOAORS l1 COOS +28 167 ADO, LIS +>Quackle: EGNOPRS m3 SPONGER +92 187 ADOS, LISP +>David: AORWAVA 6c AVOW +37 204 OBE, WET +>Quackle: AEFMOVZ 8l MEZE +54 241 +>David: AARTUNY d8 JAUNTY +32 236 +>Quackle: ACFIOOV 1l COOF +27 268 +>David: WALTIER 4c WAILED +20 256 +>Quackle: AACEINV 3a VIA +22 290 AW +>David: IRUTRUT a3 VIRTU +9 265 +>Quackle: AACEHLN 8a EH +42 332 VIRTUE +>David: QUBITUR 2b BRUIT +32 297 BI, RAW +>Quackle: AACILNR 9m RAN +16 348 ZA, EN +>David: PQUIEN? 13a QUEY +32 329 +>Quackle: CALLIER c13 EL +2 350 +>David: PINIR?N 1e PIN +11 340 PI, IT +>Quackle: ACEILOR 15a CALORIE +83 433 ELL +>David: TRAING? 14f TRAdING +67 407 TI, RE +>David: (DATSXK) +36 443 diff --git a/test/positions/boys2.gcg b/test/positions/boys2.gcg new file mode 100644 index 0000000..bbaa6f4 --- /dev/null +++ b/test/positions/boys2.gcg @@ -0,0 +1,31 @@ +#player1 David David Boys +#player2 Quackle Quackle Computer +#description David Boys plays Quackle Computer in Round 2 at the 2006 Human vs. Computer Showdown +#title 2006 Human vs. Computer Showdown Round 2 +#incomplete +>David: DOLTTPI 8g TOLD +10 10 +>Quackle: EEIMNOU 7h MEOU +17 17 MO, EL, OD +>David: HAPITE? 9b PITHEAd +70 80 TA, MOd +>Quackle: ADEINST l1 DETAINS +77 94 MEOUS +>David: NONJEV? 5j JOIN +22 102 +>Quackle: ABFLNOW d7 BATFOWL +32 126 +>David: ENAIVE? e1 NAIVEtE +72 174 BE +>Quackle: DEGNNRW 3c WRINGED +32 158 +>David: GREATQE n1 ERGATE +30 204 JOINT +>Quackle: HIMNNOP 10d FOHN +39 197 HO, EH, TAN +>David: QUISROA 1a QUOIN +45 249 +>Quackle: EIMNPTY 11a TEPOY +38 235 HOY +>David: RASBEEE 1h BARED +24 273 +>Quackle: ADIMNOY b9 PYEMIA +42 277 +>David: EESSUIR o6 REISSUE +76 349 ER +>Quackle: DINOORT 13l DOOR +18 295 REISSUER +>David: IUAICLK a14 KI +24 373 KA +>Quackle: FINSTVZ 12k ZIN +32 327 ID, NO +>David: LACUIXA n13 OI +4 377 +>Quackle: CFLRSTV l11 VIDS +8 335 +>David: GLACUXA 5b CAGE +7 384 +>Quackle: CFLRT 1n EF +15 350 +>David: LUXA n8 XU +20 404 XI, US +>Quackle: CLRT 14a KAT +7 357 +>David: LA 15n LA +9 413 OIL +>David: (CLR) +10 423 diff --git a/test/positions/boys3.gcg b/test/positions/boys3.gcg new file mode 100644 index 0000000..161ec1a --- /dev/null +++ b/test/positions/boys3.gcg @@ -0,0 +1,30 @@ +#player1 Quackle Quackle Computer +#player2 David David Boys +#description Quackle Computer plays David Boys in Round 3 at the 2006 Human vs. Computer Showdown +#title 2006 Human vs. Computer Showdown Round 3 +#incomplete +>Quackle: EIIJNTW 8g JEW +26 26 +>David: AACEENS j4 ENCASE +28 28 JEWS +>Quackle: EEIINTT k2 TINE +14 40 EN, NE +>David: AYDDRLE l4 DEARY +37 65 END, NEE +>Quackle: EINOPTY h8 EYEPOINT +92 132 +>David: HAHOLDS m8 AHOLDS +33 98 YA +>Quackle: ADIMNRS n8 MAD +30 162 YAM, HA, OD +>David: HARTOAG o9 OATH +21 119 HAO, ODA +>Quackle: BEIINRS 15a BRINIEST +83 245 +>David: AOFRGAT 14f FANG +31 150 FE, AS +>Quackle: CEGMNOQ b10 CONGER +30 275 +>David: ORATUSZ 11b OUZO +26 176 +>Quackle: IKMQTU? 13g KI +23 298 KAS +>David: TRASBRD 12d ARB +34 210 ZA, OR +>Quackle: EIMQTU? k9 MEsQUIT +88 386 EM +>David: DTRSVEP i1 PERV +18 228 VEND +>Quackle: AFLLORU h1 ORA +25 411 OP, RE, AR +>David: TDSGIWE g1 WIG +26 254 WOP, IRE, GAR +>Quackle: FLLRTUX 10f FUEL +15 426 +>David: EIODLST 6l ADO +6 260 +>Quackle: AILNRTX f2 TAR +16 442 TIRE, AGAR +>David: ESTILVU 12m DUH +7 267 +>Quackle: LOXINI? 2k TOXIN +24 466 +>David: EEILSTV 5b EVILEST +74 341 TARE +>David: (LI?) +4 345 diff --git a/test/positions/boys4.gcg b/test/positions/boys4.gcg new file mode 100644 index 0000000..762fcdb --- /dev/null +++ b/test/positions/boys4.gcg @@ -0,0 +1,28 @@ +#player1 David David Boys +#player2 Quackle Quackle Computer +#description David Boys plays Quackle Computer in Round 4 at the 2006 Human vs. Computer Showdown +#title 2006 Human vs. Computer Showdown Round 4 +#incomplete +>David: NRERXCL 8g REX +20 20 +>Quackle: ABDIOUW 7h BAUD +22 22 BE, AX +>David: ACILNRV k3 VALID +18 38 +>Quackle: EILOSWT 5h LOWLIEST +61 83 +>David: CDNRRST -CDNRT +0 38 +>Quackle: AEFIMSZ o2 MESTIZA +57 140 +>David: SRYISER 9l RYES +30 68 MESTIZAS +>Quackle: AEFINTU n1 TUFAS +31 171 UM, FE, AS +>David: RISGNH? n8 HEaRSING +77 145 HA +>Quackle: EIINORW l8 WRIER +24 195 +>David: EEOOUAB k10 OBOE +24 169 OI, BE, OR +>Quackle: ECINOOP 15h COOPING +39 234 +>David: AEEUGNT 4d TEGUA +17 186 AL +>Quackle: EOQTUV? h1 EQUAL +42 276 +>David: ENDNIRL l4 LIN +12 198 AL, IN +>Quackle: CETOTV? 1a COrVETTE +98 374 +>David: ADEILNR e3 RENAILED +68 266 +>Quackle: AAHIJRM d9 HIJRA +38 412 HE, ID +>David: DPFKNTY 13c FAD +14 280 +>Quackle: ADEEMNO 11k BEER +6 418 +>David: KYNAPOT 6b TOKAY +22 302 +>Quackle: ADEGMNO b3 MONTAGED +66 484 +>Quackle: (PAIN) +12 496 diff --git a/test/positions/boys5.gcg b/test/positions/boys5.gcg new file mode 100644 index 0000000..3c56f99 --- /dev/null +++ b/test/positions/boys5.gcg @@ -0,0 +1,28 @@ +#player1 Quackle Quackle Computer +#player2 David David Boys +#description Quackle Computer plays David Boys in Round 5 at the 2006 Human vs. Computer Showdown +#title 2006 Human vs. Computer Showdown Round 5 +#incomplete +>Quackle: HERMINE 8d HEMIN +28 28 +>David: EIPORTU h1 ERUPTION +89 89 +>Quackle: EIKRTT? i8 sKITTER +76 104 HEMINs +>David: ADEFIUX 11e FIXATED +72 161 +>Quackle: EEDGHS? 15c HEDGErS +97 201 sKITTERS +>David: AELRRUY 4h PURELY +22 183 +>Quackle: AFLMNNV 10g ALIF +39 240 AX, LA, FE +>David: ABIPRWW 3k WAB +37 220 WE, AL, BY +>Quackle: AJMNNOV 2f JORAM +36 276 +>David: ILNPRSW e5 WIPER +20 240 +>Quackle: CDGNNOV f10 CION +28 304 CALIF +>David: BELNOSY 14b YOB +25 265 OH, BE +>Quackle: ADEGNTV 6b DEVIATING +74 378 +>David: AEILNRS b1 ISLANDER +68 333 +>Quackle: AADINRU 1j ANURIA +25 403 AM +>David: GNOSUVZ 1a GINZO +75 408 +>Quackle: DEIIOST 3b LOTI +8 411 +>David: LOOSTUV 13a LOO +23 431 OY, OOH +>Quackle: CDEEIQS 4a QADI +55 466 OD, TI +>David: AESTUV a8 AVE +24 455 AR +>Quackle: CEES 5l SEC +16 482 ALS, BYE +>David: STU n5 CUTS +8 463 +>David: (E) +2 465 diff --git a/test/positions/consonant_heaviness_tendency.gcg b/test/positions/consonant_heaviness_tendency.gcg new file mode 100644 index 0000000..cb2156a --- /dev/null +++ b/test/positions/consonant_heaviness_tendency.gcg @@ -0,0 +1,20 @@ +#player1 NoName New Player 2 +#player2 NoName jasonkb +>NoName: ABELNUW 8D BLAWN +26 26 +>NoName: ADRSTYZ F8 ADZ +33 33 +>NoName: EEFGRTU 11E REFUGE +34 60 +>NoName: AEERSTY K10 YE +32 65 +>NoName: AEELQTT 9I QAT +28 88 +>NoName: AEERSTW 10J WYE +31 96 +>NoName: EEIILOT 9D EIDE +18 106 +>NoName: AEEGRST M8 RESTAGE +78 174 +>NoName: ILNNOST 14H INSOLENT +70 176 +>NoName: ?CDEIMS 15C MISCoDE +97 271 +>NoName: EHILRUX 12L EAUX +38 214 +>NoName: AGKNOPU 8L GROK +33 304 +>NoName: CEHILRV 13B CHERVIL +86 300 +>NoName: AHJNPSU 10B JUN +31 335 +>NoName: ADFIINO 15K FIDO +29 329 +>NoName: AHMNPST 12C AM +21 356 +>NoName: AINORRY L2 ROARING +16 345 +#rack2 BHNPRST diff --git a/test/positions/deadwoodendgame.gcg b/test/positions/deadwoodendgame.gcg new file mode 100644 index 0000000..fb87f03 --- /dev/null +++ b/test/positions/deadwoodendgame.gcg @@ -0,0 +1,26 @@ +#player1 jasonkb jasonkb +#player2 MartyGabriel Marty Gabriel +>jasonkb: DHNNSTW -HWND +0 0 +>MartyGabriel: DFINU 8D FUNDI +26 26 +>jasonkb: HKNNSTW E6 TH.NKS +26 26 +>MartyGabriel: EX 7G EX +23 49 +>jasonkb: AAGNOUW 10B WAU. +19 45 +>MartyGabriel: BENORT 6B BET.OR +20 69 +>jasonkb: AGINOPW A5 WAGON +36 81 +>MartyGabriel: DHNY C9 H.NDY +32 101 +>jasonkb: ABEIIPQ 12A QA.I +48 129 +>MartyGabriel: CIORZ C2 COZI.R +36 137 +>jasonkb: BEEILPU A11 E.UIP +48 177 +>MartyGabriel: ?AEEMRR 15A .AMpERER +83 220 +>jasonkb: BDDEELV 5G BEVEL +27 204 +>MartyGabriel: AAEERTU K5 .AUREATE +66 286 +>jasonkb: DDIILRS L10 DID +21 225 +>MartyGabriel: FO J9 OF +30 316 +>jasonkb: AILORST M5 RIALTOS +70 295 +>MartyGabriel: AEIISTV 2C .AVITIES +67 383 +>jasonkb: EEGGINS 8M .EG +12 307 +>MartyGabriel: MOO 1H MOO +31 414 +>jasonkb: ?EGINPS 14H ESPyING +82 389 +>MartyGabriel: ACLNOTY 4C .ANY +32 446 +>jasonkb: JL 9I J.. +18 407 +#rack2 CLOT diff --git a/test/positions/fivepoint.gcg b/test/positions/fivepoint.gcg new file mode 100644 index 0000000..c9a0261 --- /dev/null +++ b/test/positions/fivepoint.gcg @@ -0,0 +1,32 @@ +#player1 Ganesh Ganesh Asirvatham +#player2 Paul Paul Cleary +#description Ganesh Asirvatham plays Paul Cleary in Round 11 at the 2005 WSC +#title 2005 WSC Round 11 +#incomplete +>Ganesh: AOXJGWS 8g JAW +26 26 +>Paul: IEEKBNL h6 BEANLIKE +69 69 +>Paul: ARTEQ?G (challenge) +5 74 +>Ganesh: OXGSHYD i12 OXY +44 70 KO, EX +>Paul: ARTEQ?G 11g QI +11 85 +>Ganesh: GSHDINV 13g VEXINGS +44 114 +>Paul: ARTE?GN j2 GARNETs +76 161 JAWs +>Ganesh: DRRHYME 4h MYRRH +32 146 +>Paul: AZEARIH 10h LAZIER +35 196 +>Ganesh: DERESIU m2 RESIDUE +82 228 MYRRHS +>Paul: HAIUUTE 8l HEAT +33 229 +>Ganesh: ADOEASI 11j ODA +27 255 ZO, ID, EA +>Paul: UUIECEL 3f ECU +12 241 UM +>Ganesh: EASIDTR o1 TARDIEST +83 338 +>Paul: LIEUIWP 2c PLEW +30 271 WE +>Ganesh: BNCOAAE 1e BEANO +31 369 BE, EWE +>Paul: IIUPLFT c1 UPLIFT +22 293 +>Ganesh: ACEGOOR 9e COON +17 386 JO +>Paul: USIEOPI 1a PIU +15 308 +>Ganesh: AADEGIR 5b AFRAID +20 406 +>Paul: IEOUSNV l13 GUV +11 319 +>Ganesh: EFGMOTT e9 COFT +18 424 +>Paul: IEOSNNL d10 NOEL +19 338 NO, OF, ET +>Ganesh: ?EGMOT c11 OMEGa +29 453 OOF, MET, EL +>Paul: ISN 15l VINS +21 359 +>Paul: (T) +1 360 +>Ganesh: T (T) -1 452 diff --git a/test/positions/logan.gcg b/test/positions/logan.gcg new file mode 100644 index 0000000..b60f96f --- /dev/null +++ b/test/positions/logan.gcg @@ -0,0 +1,28 @@ +#player1 Maven Maven +#player2 AdamLogan Adam Logan +#title 1998 Exhibition Game +#description Maven plays Adam Logan in an exhibition game in 1998. This game was featured as the Annotated Game in the Scrabble News issue 144. +>Maven: ACNTVYZ 8F CAVY +24 24 +>AdamLogan: EGLNORY G6 YEARLONG +66 66 +>Maven: ADNNOTZ 6D DOZY +37 61 +>AdamLogan: ADEFOTV H13 OFT +21 87 +>Maven: AENNNOT 5B NEON +15 76 +>AdamLogan: ACDEEIV 12B DEVIANCE +96 183 +>Maven: AHINRTU 4A HURT +34 110 +>AdamLogan: DDEEMMN C7 EMENDED +26 209 +>Maven: ABEINNP 8A IAMB +33 143 +>AdamLogan: AILMTTU A1 MATH +27 236 +>Maven: EFGNNPS E10 FEIGN +18 161 +>AdamLogan: AILORTU 15H TUTORIAL +77 313 +>Maven: ABNOPS? J10 BOS +26 187 +>AdamLogan: IILPRSU 15A PILIS +34 347 +>Maven: AKNPRS? K5 SPANKeR +105 292 +>AdamLogan: EEEORSU B1 OE +12 359 +>Maven: HJTTWW? 7J JAW +13 305 +>AdamLogan: AEEGRSU M3 GREASE +31 390 +>Maven: HRTTWX? 6M AX +25 330 +>AdamLogan: EIIILQU O5 LEI +13 403 +>Maven: AHRTTW? 9B WE +10 340 +>AdamLogan: AIIIOQU J2 QUAI +35 438 +>Maven: AHRTTU? 1A MOUTHpART +92 432 +>Maven: (EIIO) +8 440 diff --git a/test/positions/logan_we.gcg b/test/positions/logan_we.gcg new file mode 100644 index 0000000..bb704dd --- /dev/null +++ b/test/positions/logan_we.gcg @@ -0,0 +1,25 @@ +#player1 Maven Maven +#player2 AdamLogan Adam Logan +#title 1998 Exhibition Game +#description Maven plays Adam Logan in an exhibition game in 1998. This game was featured as the Annotated Game in the Scrabble News issue 144. +>Maven: ACNTVYZ 8F CAVY +24 24 +>AdamLogan: EGLNORY G6 YE.RLONG +66 66 +>Maven: ADNNOTZ 6D DOZ. +37 61 +>AdamLogan: ADEFOTV H13 OFT +21 87 +>Maven: AENNNOT 5B NEON +15 76 +>AdamLogan: ACDEEIV 12B DEVIA.CE +96 183 +>Maven: AHINRTU 4A HURT +34 110 +>AdamLogan: DDEEMMN C7 EMEND.D +26 209 +>Maven: ABEINNP 8A IA.B +33 143 +>AdamLogan: AILMTTU A1 MAT. +27 236 +>Maven: EFGNNPS E10 FE.GN +18 161 +>AdamLogan: AILORTU 15H .UTORIAL +77 313 +>Maven: ?ABNOPS J10 BOS +26 187 +>AdamLogan: IILPRSU 15A PILIS +34 347 +>Maven: ?AKNPRS K5 SPANKeR +105 292 +>AdamLogan: EEEORSU B1 OE +12 359 +>Maven: ?HJTTWW 7J J.W +13 305 +>AdamLogan: AEEGRSU M3 GREASE +31 390 +>Maven: ?HRTTWX 6M .X +25 330 +>AdamLogan: EIIILQU O5 LEI +13 403 +#rack1 ?AHRTTW diff --git a/test/positions/multiplayer-crash.gcg b/test/positions/multiplayer-crash.gcg new file mode 100644 index 0000000..a3bdb45 --- /dev/null +++ b/test/positions/multiplayer-crash.gcg @@ -0,0 +1,22 @@ +#player1 Mark_Ogden Mark Ogden
+#player2 Chappy Chappy
+#player3 Nicole_Gorham Nicole Gorham
+#player4 Daniel_Southam Daniel Southam
+>Mark_Ogden: BCEIP 8F BICEP +22 22
+>Chappy: JLOORSU F6 JO. +28 28
+>Nicole_Gorham: BCDEITZ G7 F.Z +38 38
+>Daniel_Southam: EIORRTV J6 RA.INE +12 12
+>Mark_Ogden: EIMNRSU K2 GAWKER +32 54
+>Chappy: LNORSUY 12J SOURLY +35 63
+>Nicole_Gorham: ?DIMMOV 11D MIsMOV.D +116 154
+>Daniel_Southam: ADEILR O6 READIL. +33 45
+>Mark_Ogden: DIINQSU 9I X. +34 88
+>Chappy: CENOPRU N1 POUNCER +82 145
+>Nicole_Gorham: AFIOTTY O1 ANTE +34 188
+>Daniel_Southam: AEEEIOV 12C QAT +34 79
+>Mark_Ogden: EHOT N10 HE.OT +47 135
+>Chappy: AABEENT L4 EAVE +39 184
+>Nicole_Gorham: AFIOTWY 13B NIT +33 221
+>Daniel_Southam: AEEEILO 14D SEISING +70 149
+>Mark_Ogden: DFI 15F FID +37 172
+#rack2 AABLLNS
diff --git a/test/positions/one_tile_play.gcg b/test/positions/one_tile_play.gcg new file mode 100644 index 0000000..a4b19c6 --- /dev/null +++ b/test/positions/one_tile_play.gcg @@ -0,0 +1,5 @@ +#player1 jasonkb jasonkb +#player2 bricap bricap +>jasonkb: EHY 8F HEY +18 18 +>bricap: AEZ F8 HAZE +36 36 +#rack1 GNX diff --git a/test/positions/preendgame.gcg b/test/positions/preendgame.gcg new file mode 100644 index 0000000..209feae --- /dev/null +++ b/test/positions/preendgame.gcg @@ -0,0 +1,23 @@ +#player1 jasonkb jasonkb +#player2 MartyGabriel Marty Gabriel +>jasonkb: DHNNSTW -HWND +0 0 +>MartyGabriel: DFINU 8D FUNDI +26 26 +>jasonkb: HKNNSTW E6 THUNKS +26 26 +>MartyGabriel: EX 7G EX +23 49 +>jasonkb: AAGNOUW 10B WAUK +19 45 +>MartyGabriel: BENORT 6B BETTOR +20 69 +>jasonkb: AGINOPW A5 WAGON +36 81 +>MartyGabriel: DHNY C9 HANDY +32 101 +>jasonkb: ABEIIPQ 12A QADI +48 129 +>MartyGabriel: CIORZ C2 COZIER +36 137 +>jasonkb: BEEILPU A11 EQUIP +48 177 +>MartyGabriel: ?AEEMRR 15A PAMpERER +83 220 +>jasonkb: BDDEELV 5G BEVEL +27 204 +>MartyGabriel: AAEERTU K5 LAUREATE +66 286 +>jasonkb: DDIILRS L10 DID +21 225 +>MartyGabriel: FO J9 OF +30 316 +>jasonkb: AILORST M5 RIALTOS +70 295 +>MartyGabriel: AEIISTV 2C CAVITIES +67 383 +>jasonkb: EEGGINS 8M LEG +12 307 +>MartyGabriel: MOO 1H MOO +31 414 +#rack1 ?EGINPS diff --git a/test/positions/preendgame2.gcg b/test/positions/preendgame2.gcg new file mode 100644 index 0000000..66ca1fe --- /dev/null +++ b/test/positions/preendgame2.gcg @@ -0,0 +1,24 @@ +#player1 jasonkb jasonkb +#player2 MartyGabriel Marty Gabriel +>jasonkb: DHNNSTW -HWND +0 0 +>MartyGabriel: DFINU 8D FUNDI +26 26 +>jasonkb: HKNNSTW E6 THUNKS +26 26 +>MartyGabriel: EX 7G EX +23 49 +>jasonkb: AAGNOUW 10B WAUK +19 45 +>MartyGabriel: BENORT 6B BETTOR +20 69 +>jasonkb: AGINOPW A5 WAGON +36 81 +>MartyGabriel: DHNY C9 HANDY +32 101 +>jasonkb: ABEIIPQ 12A QADI +48 129 +>MartyGabriel: CIORZ C2 COZIER +36 137 +>jasonkb: BEEILPU A11 EQUIP +48 177 +>MartyGabriel: ?AEEMRR 15A PAMpERER +83 220 +>jasonkb: BDDEELV 5G BEVEL +27 204 +>MartyGabriel: AAEERTU K5 LAUREATE +66 286 +>jasonkb: DDIILRS L10 DID +21 225 +>MartyGabriel: FO J9 OF +30 316 +>jasonkb: AILORST M5 RIALTOS +70 295 +>MartyGabriel: AEIISTV 2C CAVITIES +67 383 +>jasonkb: EEGGINS 8M LEG +12 307 +>MartyGabriel: MOO 1H MOO +31 414 +>jasonkb: ?EGINPS 5G BEVELER +12 319 +#rack2 ACLLNOT diff --git a/test/positions/preendgame4.gcg b/test/positions/preendgame4.gcg new file mode 100644 index 0000000..02914b2 --- /dev/null +++ b/test/positions/preendgame4.gcg @@ -0,0 +1,23 @@ +#player1 JasonKatz-Brown Jason Katz-Brown +#player2 compy compy +>JasonKatz-Brown: DEILRTY 8F TIREDLY +80 80 +>compy: AGHKTTU L4 GUTTY +18 18 +>JasonKatz-Brown: DFLNOOR 5G ODORFUL +22 102 +>compy: AHKSSUW 7G HAW +37 55 +>JasonKatz-Brown: ?BEHINR 4C BIRcHEN +85 187 +>compy: EIKSSUZ H1 SIZED +45 100 +>JasonKatz-Brown: ABEESTV E3 BRAVE +20 207 +>compy: ADEKMSU 9G MED +34 134 +>JasonKatz-Brown: EFJOPST C3 OBJET +30 237 +>compy: ADKOOSU 8A DUSK +57 191 +>JasonKatz-Brown: AFGPQRS 2F FRIG +16 253 +>compy: AAELMOO M2 MOOLA +19 210 +>JasonKatz-Brown: AINOPQS N1 QI +30 283 +>compy: AAEILRX O1 ILEX +95 305 +>JasonKatz-Brown: ALNOPRS N6 PROLANS +78 361 +>compy: AAEPRUV O8 PARA +36 341 +>JasonKatz-Brown: CEEINNO 1A CONINE +32 393 +>compy: EOSTTUV 12J VOTES +16 357 +>JasonKatz-Brown: AAEINUW D8 KNAWE +24 417 +>compy: CGNSTUY J12 VUG +11 368 +#rack1 ?AAEIIU diff --git a/test/positions/short_game_with_bad_moves.gcg b/test/positions/short_game_with_bad_moves.gcg new file mode 100644 index 0000000..f7ea011 --- /dev/null +++ b/test/positions/short_game_with_bad_moves.gcg @@ -0,0 +1,9 @@ +#player1 jasonkb jasonkb +#player2 bricap bricap +>jasonkb: IIJOOPS 8G SI +4 4 +>bricap: AACIMOQ 7H QI +23 23 +>jasonkb: IJOOOPT G8 .IP +6 10 +>bricap: ?AACCMO 11A MACACOs +79 102 +>jasonkb: ?DJOOOT B10 J.TO +27 37 +>bricap: AAOOSUU 14B SOU +30 132 +#rack1 ?DELOOW diff --git a/test/test.pro b/test/test.pro new file mode 100644 index 0000000..e0a0b77 --- /dev/null +++ b/test/test.pro @@ -0,0 +1,32 @@ +TEMPLATE = app +DEPENDPATH += .. ../quackleio +INCLUDEPATH += . .. + +# enable/disable debug symbols +# CONFIG += debug + +CONFIG += console +CONFIG -= x11 +CONFIG += release + +build_pass:CONFIG(debug, debug|release) { + LIBS += -L../debug -L../quackleio/debug +} + +build_pass:CONFIG(release, debug|release) { + LIBS += -L../release -L../quackleio/release +} + +LIBS += -L.. -L../quackleio -lquackle -lquackleio + +# Input +HEADERS += testharness.h trademarkedboards.h +SOURCES += testharness.cpp testmain.cpp trademarkedboards.cpp + + +win32:!win32-g++ { + QMAKE_CFLAGS_DEBUG ~= s/-MDd/-MTd/ + QMAKE_CXXFLAGS_DEBUG ~= s/-MDd/-MTd/ + QMAKE_CFLAGS_RELEASE ~= s/-MD/-MT/ + QMAKE_CXXFLAGS_RELEASE ~= s/-MD/-MT/ +} diff --git a/test/testharness.cpp b/test/testharness.cpp new file mode 100644 index 0000000..6febbb9 --- /dev/null +++ b/test/testharness.cpp @@ -0,0 +1,796 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 Jason Katz-Brown and John O'Laughlin. + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <QtCore> + +#include <iostream> +#include <limits> +#include <algorithm> + +#include <bogowinplayer.h> +#include <computerplayercollection.h> +#include <resolvent.h> +#include <datamanager.h> +#include <endgameplayer.h> +#include <game.h> +#include <gameparameters.h> +#include <lexiconparameters.h> +#include <strategyparameters.h> +#include <enumerator.h> +#include <reporter.h> + +#include <quackleio/dictimplementation.h> +#include <quackleio/flexiblealphabet.h> +#include <quackleio/froggetopt.h> +#include <quackleio/gcgio.h> +#include <quackleio/util.h> + +#include "trademarkedboards.h" +#include "testharness.h" + +using namespace Quackle; + +TestHarness::TestHarness() + : m_computerPlayerToTest(0), m_quiet(false) +{ + m_gamesDir = "games"; + m_dataManager.setComputerPlayers(Quackle::ComputerPlayerCollection::fullCollection()); +} + +TestHarness::~TestHarness() +{ +} + +const char *usage = +"Optional arguments:\n" +"--computer=; sets the computer player (default 'Speedy Player').\n" +"--mode=\n" +" 'positions' (default) runs computer player all positions.\n" +" 'report' asks computer player for report on all positions.\n" +" 'htmlreport' asks for html report on all positions.\n" +" 'selfplay' runs 1000 selfplay games.\n" +" 'playability' output info for computing playability values.\n" +" 'enumerate' lists all racks.\n" +" 'staticleaves' output static leave values of racks in 'racks' file.\n" +" 'randomracks' spit out random racks (forever?).\n" +" 'leavecalc' spit out roughish values of leaves in 'leaves' file.\n" +" 'anagram' anagrams letters supplied in --letters.\n" +"--position=game.gcg; this option can be repeated to specify positions\n" +" to test.\n" +"--lexicon=; sets the lexicon (default 'twl06').\n" +"--alphabet=; sets the alphabet (default 'english').\n" +"--seed=integer; set the random seed for reproducability.\n" +"--report; generate reports for selfplay games (default false).\n" +"--letters; letters to anagram.\n" +"--build; when mode is anagram, do not require that all letters be used.\n" +"--quiet; print nothing during selfplay games (default false).\n" +"--repetitions=integer; the number of games for selfplay (default 1000).\n"; + +void TestHarness::executeFromArguments() +{ + GetOpt opts; + + QString mode; + QString computer; + QString seedString; + QString repString; + bool build; + QString letters; + bool help; + bool report; + unsigned int seed = numeric_limits<unsigned int>::max(); + unsigned int reps = 1000; + + opts.addOption('c', "computer", &computer); + opts.addOption('a', "alphabet", &m_alphabet); + opts.addOption('l', "lexicon", &m_lexicon); + opts.addOption('m', "mode", &mode); + opts.addOption('s', "seed", &seedString); + opts.addOption('r', "repetitions", &repString); + opts.addOption('t', "letters", &letters); + opts.addRepeatableOption("position", &m_positions); + + opts.addSwitch("report", &report); + opts.addSwitch("build", &build); + opts.addSwitch("quiet", &m_quiet); + opts.addSwitch("help", &help); + + if (!opts.parse()) + return; + + if (help) + { + UVcout << usage << endl; + return; + } + + if (mode.isNull()) + mode = "positions"; + if (computer.isNull()) + computer = "Speedy Player"; + if (m_lexicon.isNull()) + m_lexicon = "twl06"; + if (m_alphabet.isNull()) + m_alphabet = "english"; + if (!seedString.isNull()) + seed = seedString.toUInt(); + if (!repString.isNull()) + reps = repString.toUInt(); + + + bool playerFound = false; + const Quackle::Player &player = QUACKLE_COMPUTER_PLAYERS.playerForName(QuackleIO::Util::qstringToString(computer), playerFound); + if (playerFound) + { + m_computerPlayerToTest = player.computerPlayer(); + } + else + { + UVcout << "Computer " << QuackleIO::Util::qstringToString(computer) << " not found!" << endl; + return; + } + + startUp(); + + if (mode == "positions") + testPositions(); + else if (mode == "report") + testReport(false); + else if (mode == "htmlreport") + testReport(true); + else if (mode == "enumerate") + enumerateAll(); + else if (mode == "staticleaves") + staticLeaves(QString("racks")); + else if (mode == "randomracks") + randomRacks(); + else if (mode == "leavecalc") + leaveCalc(QString("leaves")); + else if (mode == "anagram") + anagram(letters, build); + else if (mode == "selfplay") + selfPlayGames(seed, reps, report, false); + else if (mode == "playability") + selfPlayGames(seed, reps, report, true); + else if (mode == "worddump") + wordDump(); + else if (mode == "bingos") + bingos(); +} + +void TestHarness::startUp() +{ + UVcout << "Starting up."; + + m_dataManager.setBackupLexicon("twl06"); + m_dataManager.setDataDirectory("../data"); + + QString alphabetFile = QuackleIO::Util::stdStringToQString(Quackle::AlphabetParameters::findAlphabetFile(QuackleIO::Util::qstringToStdString(m_alphabet) + ".quackle_alphabet")); + QuackleIO::FlexibleAlphabetParameters *flexure = new QuackleIO::FlexibleAlphabetParameters; + if (flexure->load(alphabetFile)) + { + m_dataManager.setAlphabetParameters(flexure); + } + else + { + UVcerr << "Couldn't load alphabet " << QuackleIO::Util::qstringToString(m_alphabet) << endl; + delete flexure; + } + + m_dataManager.setBoardParameters(new ScrabbleBoard()); + + m_dataManager.lexiconParameters()->loadGaddag(Quackle::LexiconParameters::findDictionaryFile(QuackleIO::Util::qstringToStdString(m_lexicon + ".gaddag"))); + UVcout << "."; + + m_dataManager.lexiconParameters()->loadDawg(Quackle::LexiconParameters::findDictionaryFile(QuackleIO::Util::qstringToStdString(m_lexicon + ".dawg"))); + + m_dataManager.strategyParameters()->initialize(QuackleIO::Util::qstringToStdString(m_lexicon)); + UVcout << "."; + + UVcout << endl; + + m_gamesDir = QString("games_PLAYERNAME_%1").arg(QDateTime::currentDateTime().toString("dd.MM_hh.mm.ss")); +} + +void TestHarness::testFromFile(const QString &file) +{ + UVcout << "Testing game from " << QuackleIO::Util::qstringToString(file) << endl; + Quackle::Game *game = createNewGame(file); + if (game) + { + testPosition(game->currentPosition(), computerPlayerToTest()); + } + + delete game; +} + +double TestHarness::leaveSim(const Rack &R, int iterations) +{ + double sum = 0.0; + + UVcout << R << endl; + Bag B; + B.removeLetters(R.tiles()); + + int tilesToLeave = 14 + rand() % (93 - 14); + + for (int i = 0; i < iterations; i++) + { + Quackle::Game game; + + Quackle::PlayerList players; + + Quackle::Player compyA(m_computerPlayerToTest->name() + MARK_UV(" A"), Quackle::Player::ComputerPlayerType, 0); + compyA.setAbbreviatedName(MARK_UV("A")); + compyA.setComputerPlayer(new Quackle::StaticPlayer()); + players.push_back(compyA); + + Quackle::Player compyB(m_computerPlayerToTest->name() + MARK_UV(" B"), Quackle::Player::ComputerPlayerType, 1); + compyB.setAbbreviatedName(MARK_UV("B")); + compyB.setComputerPlayer(new Quackle::StaticPlayer()); + players.push_back(compyB); + + game.setPlayers(players); + game.associateKnownComputerPlayers(); + + game.addPosition(); + + game.currentPosition().setCurrentPlayerRack(Rack(""), true); + game.currentPosition().setOppRack(Rack(""), true); + + Bag startBag = B; + game.currentPosition().setBag(startBag); + + game.addPosition(); + + UVcout << "NEW GAME" << endl; + + while (game.currentPosition().bag().size() > tilesToLeave) + { + if (game.currentPosition().gameOver()) + { + UVcout << "GAME OVER" << endl; + break; + } + + const Quackle::Player player(game.currentPosition().currentPlayer()); + Quackle::Move compMove(game.haveComputerPlay()); + UVcout << "with " << player.rack() << ", " << player.name() << " commits to " << compMove << endl; + } + + game.currentPosition().setCurrentPlayerRack(R, true); + Quackle::Move compMove(game.haveComputerPlay()); + sum += compMove.equity; + } + + return sum / iterations; +} + +void TestHarness::leaveCalc(const QString &filename) +{ + QuackleIO::GCGIO io; + QFile file(filename); + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + UVcout << "Could not open file " << QuackleIO::Util::qstringToString(filename) << endl; + return; + } + + QTextStream in(&file); + + const int iterations = 10; + + while (!in.atEnd()) + { + QString line = in.readLine(); + Rack rack(QuackleIO::Util::encode(line)); + double value = leaveSim(rack, iterations); + UVcout << "leavecalc: " << rack << " " << value << endl; + } + + file.close(); + +} + +void TestHarness::randomRacks() +{ + for (;;) + { + Game game; + + Quackle::PlayerList players; + + Player compyA(m_computerPlayerToTest->name() + MARK_UV(" A"), Quackle::Player::ComputerPlayerType, 0); + compyA.setAbbreviatedName(MARK_UV("A")); + compyA.setComputerPlayer(m_computerPlayerToTest); + players.push_back(compyA); + + game.setPlayers(players); + + game.addPosition(); + + UVcout << game.currentPosition().currentPlayer().rack() << endl; + } +} + +void TestHarness::staticLeaves(const QString &filename) +{ + QuackleIO::GCGIO io; + QFile file(filename); + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + UVcout << "Could not open file " << QuackleIO::Util::qstringToString(filename) << endl; + return; + } + + QTextStream in(&file); + + Quackle::Game game; + + Quackle::PlayerList players; + + Quackle::Player compyA(m_computerPlayerToTest->name() + MARK_UV(" A"), Quackle::Player::ComputerPlayerType, 0); + compyA.setAbbreviatedName(MARK_UV("A")); + compyA.setComputerPlayer(m_computerPlayerToTest); + players.push_back(compyA); + + Quackle::Player compyB(m_computerPlayerToTest->name() + MARK_UV(" B"), Quackle::Player::ComputerPlayerType, 1); + compyB.setAbbreviatedName(MARK_UV("B")); + compyB.setComputerPlayer(m_computerPlayerToTest); + players.push_back(compyB); + + game.setPlayers(players); + game.associateKnownComputerPlayers(); + + game.addPosition(); + + while (!in.atEnd()) + { + QString line = in.readLine(); + Rack rack(QuackleIO::Util::encode(line)); + game.currentPosition().setCurrentPlayerRack(rack); + Move move = game.currentPosition().staticBestMove(); + //Move scoredExch = game.currentPosition().scoreMove(exchZero); + //UVcout << rack << " " << scoredExch << endl; + UVcout << rack << " " << move << endl; + } + + file.close(); +} + +void TestHarness::enumerateAll() +{ + Quackle::Bag B; + Enumerator E(B); + ProbableRackList racks; + E.enumerate(&racks); + for (ProbableRackList::iterator it = racks.begin(); it != racks.end(); ++it) + UVcout << (*it).rack << " " << (*it).probability << endl; +} + +struct PowerRack +{ + PowerRack(ProbableRack &r) : rack(r.rack), + stemProbability(r.probability), + usableTiles(0), + power(0) {} + + Rack rack; + double stemProbability; /* MSP */ + int usableTiles; /* UT */ + double power; /* MMPR */ +}; + +typedef vector<PowerRack> PowerRackList; + +static bool powerSort(const PowerRack& r1, + const PowerRack& r2) +{ + return r1.power > r2.power; +} + +// Looks for a single letter that acts to pluralize words based on the assumption +// that it will make a very common last letter in the lexicon. +static Letter findPluarlizer() +{ + // generate all the words in the lexicon + Generator gen; + int flags = Generator::AnagramRearrange | Generator::AddAnyLetters + | Generator::ClearBlanknesses; + WordList all = gen.anagramLetters("", flags); + + // count the frequency of occurrence of the final letter + const int allLetters = QUACKLE_FIRST_LETTER + QUACKLE_MAXIMUM_ALPHABET_SIZE; + int endCounts[allLetters]; + for (int j = 0; j < allLetters; j++) { + endCounts[j] = 0; + } + for (WordList::iterator it = all.begin(); it != all.end(); ++it) { + LetterString word = *it; + Letter last = word[word.length() - 1]; + endCounts[last]++; + } + + // find the most common and 2nd most common final letters + int maxEndCnt = 0; + int maxEndLetter = QUACKLE_NULL_MARK; + int max2EndCnt = 0; + int max2EndLetter = QUACKLE_NULL_MARK; + for (int j = 0; j < allLetters; j++) { + if (endCounts[j] > maxEndCnt) { + max2EndCnt = maxEndCnt; + max2EndLetter = maxEndLetter; + maxEndCnt = endCounts[j]; + maxEndLetter = j; + } else if (endCounts[j] > max2EndCnt) { + max2EndCnt = endCounts[j]; + max2EndLetter = j; + } + } + +// UVcout << QUACKLE_ALPHABET_PARAMETERS->userVisible(maxEndLetter) +// << " " << endCounts[maxEndLetter] << endl; +// UVcout << QUACKLE_ALPHABET_PARAMETERS->userVisible(max2EndLetter) +// << " " << endCounts[max2EndLetter] << endl; + + // We found a pluralizer if it is much more common, otherwise this + // language doesn't seem to have one. + if (endCounts[maxEndLetter] > 2 * endCounts[max2EndLetter]) { + UVcout << QUACKLE_ALPHABET_PARAMETERS->userVisible(maxEndLetter) + << " is the pluralizer" << endl; + return maxEndLetter; + } + return QUACKLE_NULL_MARK; +} + +void TestHarness::bingos() +{ + Letter pluralizer = findPluarlizer(); + + // enumerate all racks not considering blanks + Quackle::Bag B; + int blankCnt = 0; + while (B.removeLetter(QUACKLE_BLANK_MARK)) { + ++blankCnt; + } + Enumerator E(B); + ProbableRackList enumRacks; + E.enumerate(&enumRacks, 6); + + // convert probable racks to power racks for extra fields + PowerRackList racks; + for (ProbableRackList::iterator it = enumRacks.begin(); + it != enumRacks.end(); ++it) { + racks.push_back(PowerRack(*it)); + } + + // get counts of all non-blank letters + Letter start = QUACKLE_ALPHABET_PARAMETERS->firstLetter(); + Letter end = QUACKLE_ALPHABET_PARAMETERS->lastLetter(); + char bagCounts[QUACKLE_FIRST_LETTER + QUACKLE_MAXIMUM_ALPHABET_SIZE]; + B.letterCounts(bagCounts); + + Generator gen; + int cnt = 0; + for (PowerRackList::iterator it = racks.begin(); it != racks.end(); ++it) { + LetterString letters(it->rack.tiles()); + + char rackCounts[QUACKLE_FIRST_LETTER + QUACKLE_MAXIMUM_ALPHABET_SIZE]; + String::counts(letters, rackCounts); + + if (++cnt % 1000 == 0) { + UVcout << "stem: " << QUACKLE_ALPHABET_PARAMETERS->userVisible(letters) + << " (" << cnt << " / " << racks.size() << ")" << endl; + } + + // Count how many tiles combine with rack to make a bingo + int usableTiles = 0; + for (Letter c = start; c <= end; ++c) { + if (bagCounts[c] <= rackCounts[c]) { + continue; // none left in bag to draw + } + int flags = Generator::AnagramRearrange | Generator::SingleMatch; + WordList anagrams = gen.anagramLetters(letters + c, flags); + if (anagrams.size() > 0) { + usableTiles += bagCounts[c] - rackCounts[c]; // count all in the bag + } + } + + // if we can make anything we can also do so with blanks + if (usableTiles > 0) { + usableTiles += blankCnt; + } + it->usableTiles = usableTiles; + if (rackCounts[pluralizer]) { + // Baron: "However, due to the frequent retention of the S in + // actual play, its attributed frequency was subsequently increased + // artificially 50%". I generalized 'S' to a pluralizer for + // multi-language support. + it->stemProbability *= 1.5; + } + it->power = it->stemProbability * usableTiles; + } + + sort(racks.begin(), racks.end(), powerSort); + + // Normalize max MSP to 1 (or 1.5 if it contains pluralizer) and MMPR + double scale = 1 / racks.begin()->stemProbability; + if (racks.begin()->rack.contains(LetterString(1, pluralizer))) { + scale *= 1.5; + } + for (PowerRackList::iterator it = racks.begin(); it != racks.end(); ++it) { + it->stemProbability *= scale; + it->power *= scale; + } + + // dump results + for (PowerRackList::iterator it = racks.begin(); it != racks.end(); ++it) { + UVcout << it->rack << " " << it->stemProbability << " " + << it->usableTiles << " " << it->power << endl; + } +} + +void TestHarness::testPosition(const Quackle::GamePosition &position, Quackle::ComputerPlayer *player) +{ + player->setPosition(position); + + ProbableRackList racks; + Quackle::Bag unseenBag = position.unseenBag(); + + if (unseenBag.size() <= QUACKLE_PARAMETERS->rackSize() + 3) + { + Enumerator enumerator(unseenBag); + enumerator.enumerate(&racks); + UVcout << racks.size() << " enumerations: " << endl; + for (ProbableRackList::iterator it = racks.begin(); it != racks.end(); ++it) + UVcout << (*it).rack << " " << (*it).probability << endl; + } + + const int movesToShow = 10; + UVcout << "Testing " << computerPlayerToTest()->name() << " on:" << endl; + UVcout << position << endl; + UVcout << "Generating moves..." << endl; + + MoveList moves = computerPlayerToTest()->moves(movesToShow); + + for (Quackle::MoveList::const_iterator it = moves.begin(); it != moves.end(); ++it) + { + UVcout << *it << endl; + } +} + +void TestHarness::anagram(const QString &letters, bool build) +{ + QuackleIO::DictImplementation dict; + Dict::WordList list = dict.query(letters, build? Dict::Querier::NoRequireAllLetters : Dict::Querier::None); + + for (Dict::WordList::Iterator it = list.begin(); it != list.end(); ++it) + { + UVcout << QuackleIO::Util::qstringToString((*it).word); + if ((*it).british) + UVcout << "#"; + UVcout << endl; + } +} + +Quackle::Game *TestHarness::createNewGame(const QString &filename) +{ + QuackleIO::GCGIO io; + QFile file(filename); + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + UVcout << "Could not open gcg " << QuackleIO::Util::qstringToString(filename) << endl; + return 0; + } + + QTextStream in(&file); + Quackle::Game *game = io.read(in, QuackleIO::Logania::MaintainBoardPreparation); + file.close(); + + return game; +} + +void TestHarness::testPositions() +{ + UVcout << "Testing " << m_positions.size() << " positions with " << m_computerPlayerToTest->name() << "." << endl; + for (QStringList::iterator it = m_positions.begin(); it != m_positions.end(); ++it) + testFromFile(*it); +} + +void TestHarness::testReport(bool html) +{ + UVcout << "Reporting on " << m_positions.size() << " positions with " << m_computerPlayerToTest->name() << "." << endl; + for (QStringList::iterator it = m_positions.begin(); it != m_positions.end(); ++it) + { + Quackle::Game *game = createNewGame(*it); + if (game) + { + if (html) + { + UVcout << game->currentPosition().board().htmlBoard(45) << endl; + } + else + { + UVString report; + Quackle::Reporter::reportGame(*game, m_computerPlayerToTest, &report); + UVcout << report << endl; + } + } + + delete game; + } +} + +void TestHarness::selfPlayGames(unsigned int seed, unsigned int reps, bool reports, bool playability) +{ + if (seed != numeric_limits<unsigned int>::max()) { + UVcout << "using seed " << seed << endl; + m_dataManager.seedRandomNumbers(seed); + } + + for (unsigned int i = 0; i < reps; i++) + selfPlayGame(i, reports, playability); +} + +void TestHarness::selfPlayGame(unsigned int gameNumber, bool reports, bool playability) +{ + Quackle::Game game; + + Quackle::PlayerList players; + + Quackle::Player compyA(m_computerPlayerToTest->name() + MARK_UV(" A"), Quackle::Player::ComputerPlayerType, 0); + compyA.setAbbreviatedName(MARK_UV("A")); + compyA.setComputerPlayer(m_computerPlayerToTest); + players.push_back(compyA); + + Quackle::Player compyB(m_computerPlayerToTest->name() + MARK_UV(" B"), Quackle::Player::ComputerPlayerType, 1); + compyB.setAbbreviatedName(MARK_UV("B")); + compyB.setComputerPlayer(m_computerPlayerToTest); + players.push_back(compyB); + + game.setPlayers(players); + game.associateKnownComputerPlayers(); + + game.addPosition(); + + UVcout << "NEW GAME (#" << gameNumber << ")" << endl; + + QTime time; + time.start(); + + const int playahead = 50; + int i; + for (i = 0; i < playahead; ++i) + { + if (game.currentPosition().gameOver()) + { + if (!m_quiet) { UVcout << "GAME OVER" << endl; } + break; + } + + const Quackle::Player player(game.currentPosition().currentPlayer()); + + if (!m_quiet) { + if (playability) { + game.currentPosition().kibitz(100); + Quackle::MoveList moves = game.currentPosition().moves(); + float bestEquity = moves.front().equity - 0.0001f; + Quackle::MoveList tops; + for (MoveList::iterator it = moves.begin(); it != moves.end(); ++it) { + if ((*it).equity >= bestEquity) { + tops.push_back(*it); + } + } + int numTops = tops.size(); + for (MoveList::iterator it = tops.begin(); it != tops.end(); ++it) { + MoveList words = game.currentPosition().allWordsFormedBy(*it); + for (MoveList::iterator it2 = words.begin(); it2 != words.end(); ++it2) { + Rack word = (*it2).prettyTiles(); + UVcout << word << " " << numTops << endl; + } + } + int toPlay = rand() % numTops; + //UVcout << "playing move #" << toPlay << endl; + game.commitMove(tops[toPlay]); + } else { + Quackle::Move compMove(game.haveComputerPlay()); + UVcout << "with " << player.rack() << ", " << player.name() + << " commits to " << compMove << endl; + } + } + } + + int secondsElapsed = static_cast<int>(time.elapsed() / 1000); + if (!m_quiet) { + UVcout << "Game " << gameNumber << " played in " << secondsElapsed + << " seconds with " << i << " moves" << endl; + } + + if (!reports) { + return; + } + + Quackle::StaticPlayer playah; + UVString report; + Quackle::Reporter::reportGame(game, &playah, &report); + if (!m_quiet) { UVcout << report << endl; } + + QString gamesDir = m_gamesDir; + gamesDir.replace("PLAYERNAME", QuackleIO::Util::uvStringToQString(m_computerPlayerToTest->name())); + gamesDir.replace(" ", "_"); + QDir::current().mkdir(gamesDir); + + QString joinedCompyName = QuackleIO::Util::uvStringToQString(m_computerPlayerToTest->name()); + joinedCompyName.replace(" ", "_"); + QFile outFile(QString("%1/%2-game-%3.gcg").arg(gamesDir).arg(joinedCompyName).arg(gameNumber)); + + if (!outFile.open(QIODevice::WriteOnly | QIODevice::Text)) + { + UVcout << "Could not open gcg output file" << endl; + return; + } + + QuackleIO::GCGIO io; + QTextStream out(&outFile); + io.write(game, out); + + QFile outFileReport(QString("%1/%2-game-%3.report").arg(gamesDir).arg(joinedCompyName).arg(gameNumber)); + + if (!outFileReport.open(QIODevice::WriteOnly | QIODevice::Text)) + { + UVcout << "Could not open report output file" << endl; + return; + } + QTextStream outReport(&outFileReport); + outReport << QuackleIO::Util::uvStringToQString(report); + outReport << "Game played in " << secondsElapsed << " seconds." << endl; + + outFile.close(); + outFileReport.close(); +} + +static void dumpGaddag(const GaddagNode *node, const LetterString &prefix) +{ + for (const GaddagNode* child = node->firstChild(); child; child = child->nextSibling()) { + Letter childLetter = child->letter(); + LetterString newPrefix(prefix); + newPrefix += childLetter; + + if (child->isTerminal()) { + UVcout << "wordDump: " << QUACKLE_ALPHABET_PARAMETERS->userVisible(newPrefix) << endl; + } + if (child->firstChild()) { + dumpGaddag(child, newPrefix); + } + } +} + +void TestHarness::wordDump() +{ + if (QUACKLE_LEXICON_PARAMETERS->hasGaddag()) { + dumpGaddag(QUACKLE_LEXICON_PARAMETERS->gaddagRoot(), + LetterString()); + } else { + UVcout << "wordDump: no gaddag" << endl; + } +} diff --git a/test/testharness.h b/test/testharness.h new file mode 100644 index 0000000..aa3e139 --- /dev/null +++ b/test/testharness.h @@ -0,0 +1,117 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 Jason Katz-Brown and John O'Laughlin. + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef QUACKLE_TESTHARNESS_H +#define QUACKLE_TESTHARNESS_H + +#include <QStringList> + +#include <datamanager.h> +#include <alphabetparameters.h> +namespace Quackle +{ + class ComputerPlayer; + class Game; + class GamePosition; + class Rack; + class GaddagNode; +} + +class TestHarness +{ +public: + TestHarness(); + ~TestHarness(); + + // parse and execute commands specified on command line + void executeFromArguments(); + + void startUp(); + + // Loads all positions and runs the computer player on them. + void testPositions(); + + // Loads all positions and spits out a report on them. + void testReport(bool html); + + // Enumerates all racks using a full bag + void enumerateAll(); + + // Compute bingo stems ala Baron's MMPR + void bingos(); + + // Loads game from the file, and tests the final position, + // and cleans up the game. + void testFromFile(const QString &file); + + // Load racks from a file racks and spit out their static leave values + void staticLeaves(const QString &file); + + // Spit out random racks. + void randomRacks(); + + // Spit out roughish leave values. Leaves come from file leaves. + void leaveCalc(const QString &file); + + // Sim a leave. + double leaveSim(const Quackle::Rack &R, int iterations); + + // Tests what the computer player does on this position. + void testPosition(const Quackle::GamePosition &position, Quackle::ComputerPlayer *player); + + // Anagrams given letters. + void anagram(const QString &letters, bool build); + + void wordDump(); + + // Allocates and loads a game from the file. + Quackle::Game *createNewGame(const QString &filename); + + void selfPlayGames(unsigned int seed, unsigned int reps, bool reports, bool playability); + void selfPlayGame(unsigned int gameNumber, bool reports, bool playability); + + // Sets the positions that will be tested. + void setPositions(const QStringList &positions) + { + m_positions = positions; + } + + Quackle::ComputerPlayer *computerPlayerToTest() const + { + return m_computerPlayerToTest; + } + + void setComputerPlayerToTest(Quackle::ComputerPlayer *computerPlayer) + { + m_computerPlayerToTest = computerPlayer; + } + +protected: + // void dumpGaddag(const GaddagNode *node, const LetterString &prefix); + QStringList m_positions; + Quackle::DataManager m_dataManager; + Quackle::ComputerPlayer *m_computerPlayerToTest; + bool m_quiet; + QString m_gamesDir; + QString m_lexicon; + QString m_alphabet; +}; + +#endif diff --git a/test/testmain.cpp b/test/testmain.cpp new file mode 100644 index 0000000..f304c2a --- /dev/null +++ b/test/testmain.cpp @@ -0,0 +1,39 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 Jason Katz-Brown and John O'Laughlin. + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include <QCoreApplication> +#include <QStringList> + +#include <game.h> +#include <quackleio/util.h> + +#include "testharness.h" +#include "trademarkedboards.h" + +int main(int argc, char **argv) +{ + QCoreApplication a(argc, argv); + + TestHarness harness; + harness.executeFromArguments(); + + return 0; +} + diff --git a/test/trademarkedboards.cpp b/test/trademarkedboards.cpp new file mode 100644 index 0000000..b9eb37c --- /dev/null +++ b/test/trademarkedboards.cpp @@ -0,0 +1,148 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 Jason Katz-Brown and John O'Laughlin. + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#include "trademarkedboards.h" + +ScrabbleBoard::ScrabbleBoard() +{ + m_name = MARK_UV("Scrabble Board"); + + const int letterm[15][15] = + { + // A B C D E F G H I J K L M N O + {1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1}, + {1, 1, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1}, + {2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 3, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 3, 1}, + {1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 1, 1, 2, 1, 1}, + {1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1}, + {1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 1, 1, 2, 1, 1}, + {1, 3, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 3, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2}, + {1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 1, 1}, + {1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1} + }; + + const int wordm[15][15] = + { + // A B C D E F G H I J K L M N O + {3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3}, + {1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1}, + {1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1}, + {1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1}, + {1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1}, + {1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1}, + {1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1}, + {1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1}, + {3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3} + }; + + for (int i = 0; i < 15; ++i) + { + for (int j = 0; j < 15; ++j) + { + m_letterMultipliers[i][j] = letterm[i][j]; + m_wordMultipliers[i][j] = wordm[i][j]; + } + } +} + +////// + +SuperScrabbleBoard::SuperScrabbleBoard() +{ + m_height = 21; + m_width = 21; + + m_startRow = 10; + m_startColumn = 10; + + m_name = MARK_UV("Super Scrabble Board"); + + const int letterm[21][21] = + { + // A B C D E F G H I J K L M N O P Q R S T U + {1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1}, + {1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1}, + {2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2}, + {1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1}, + {1, 1, 4, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 4, 1, 1}, + {1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1}, + {2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2}, + {1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1}, + {1, 1, 4, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 4, 1, 1}, + {1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1}, + {2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2}, + {1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1}, + {1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1} + }; + const int wordm[21][21] = + { + // A B C D E F G H I J K L M N O P Q R S T U + {4, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 4}, + {1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1}, + {1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1}, + {1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1}, + {1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1}, + {3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3}, + {1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1}, + {1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1}, + {1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1}, + {1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1}, + {1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1}, + {3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3}, + {1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1}, + {1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1}, + {1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1}, + {1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1}, + {4, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 4}, + }; + + for (int i = 0; i < 21; ++i) + { + for (int j = 0; j < 21; ++j) + { + m_letterMultipliers[i][j] = letterm[i][j]; + m_wordMultipliers[i][j] = wordm[i][j]; + } + } +} diff --git a/test/trademarkedboards.h b/test/trademarkedboards.h new file mode 100644 index 0000000..f596c8f --- /dev/null +++ b/test/trademarkedboards.h @@ -0,0 +1,40 @@ +/* + * Quackle -- Crossword game artificial intelligence and analysis tool + * Copyright (C) 2005-2006 Jason Katz-Brown and John O'Laughlin. + * + * 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 2 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef QUACKLE_TRADEMARKEDBOARDS_H +#define QUACKLE_TRADEMARKEDBOARDS_H + +#include "boardparameters.h" + +// Name: Scrabble Board +class ScrabbleBoard : public Quackle::BoardParameters +{ +public: + ScrabbleBoard(); +}; + +// Name: Super Scrabble Board +class SuperScrabbleBoard : public Quackle::BoardParameters +{ +public: + SuperScrabbleBoard(); +}; + +#endif |