From 81da7fe341fd14c69c06419328a403c36422a054 Mon Sep 17 00:00:00 2001 From: Neil Smith Date: Fri, 25 Sep 2009 16:27:26 +0100 Subject: [PATCH] Initial commit --- README | 3 + Rakefile | 8 + game-lines-11.txt | 166 + game-lines-12.txt | 166 + game-lines-13.txt | 169 + game-lines-15.txt | 172 + game-lines-16.txt | 172 + game-lines-17.txt | 175 + game-lines-18.txt | 178 + game-lines-19.txt | 178 + game-lines-20.txt | 181 + game-lines-21.txt | 184 ++ game-lines-8.txt | 160 + lib/.LCKlibcartagena.rb~ | 1 + lib/.svn/all-wcprops | 35 + lib/.svn/entries | 95 + lib/.svn/format | 1 + lib/.svn/prop-base/first-move.rb.svn-base | 5 + lib/.svn/prop-base/random-move.rb.svn-base | 5 + lib/.svn/text-base/first-move.rb.svn-base | 26 + lib/.svn/text-base/game_handler.rb.svn-base | 59 + .../text-base/libcartagena.rb.netbeans-base | 726 ++++ lib/.svn/text-base/libcartagena.rb.svn-base | 722 ++++ lib/.svn/text-base/main.rb.svn-base | 6 + .../text-base/random-move.rb.netbeans-base | 27 + lib/.svn/text-base/random-move.rb.svn-base | 27 + lib/.svn/tmp/tempfile.2.tmp | 497 +++ lib/.svn/tmp/tempfile.3.tmp | 503 +++ lib/.svn/tmp/tempfile.4.tmp | 510 +++ lib/.svn/tmp/tempfile.5.tmp | 707 ++++ lib/.svn/tmp/tempfile.6.tmp | 704 ++++ lib/.svn/tmp/tempfile.tmp | 508 +++ lib/cgi/.svn/all-wcprops | 11 + lib/cgi/.svn/entries | 39 + lib/cgi/.svn/format | 1 + .../text-base/cartagena-simple.rb.svn-base | 150 + lib/cgi/cartagena-simple.rb | 150 + lib/first-move.rb | 26 + lib/game_handler.rb | 59 + lib/html/.svn/all-wcprops | 167 + lib/html/.svn/entries | 372 +++ lib/html/.svn/format | 1 + .../.svn/prop-base/blue-pirate.gif.svn-base | 5 + .../.svn/prop-base/blue_pirate.png.svn-base | 5 + lib/html/.svn/prop-base/boat.gif.svn-base | 5 + lib/html/.svn/prop-base/boat.png.svn-base | 5 + lib/html/.svn/prop-base/bottle.jpg.svn-base | 5 + lib/html/.svn/prop-base/bottle.png.svn-base | 5 + .../.svn/prop-base/brown_pirate.png.svn-base | 5 + lib/html/.svn/prop-base/cell.png.svn-base | 5 + lib/html/.svn/prop-base/dagger.jpg.svn-base | 5 + lib/html/.svn/prop-base/dagger.png.svn-base | 5 + .../.svn/prop-base/green_pirate.png.svn-base | 5 + lib/html/.svn/prop-base/gun.jpg.svn-base | 5 + lib/html/.svn/prop-base/gun.png.svn-base | 5 + lib/html/.svn/prop-base/hat.jpg.svn-base | 5 + lib/html/.svn/prop-base/hat.png.svn-base | 5 + lib/html/.svn/prop-base/keys.jpg.svn-base | 5 + lib/html/.svn/prop-base/keys.png.svn-base | 5 + .../.svn/prop-base/red_pirate.png.svn-base | 5 + lib/html/.svn/prop-base/retreat.png.svn-base | 5 + lib/html/.svn/prop-base/skull.jpg.svn-base | 5 + lib/html/.svn/prop-base/skull.png.svn-base | 5 + .../.svn/prop-base/yellow_pirate.png.svn-base | 5 + .../.svn/text-base/blue-pirate.gif.svn-base | Bin 0 -> 982 bytes .../.svn/text-base/blue_pirate.png.svn-base | Bin 0 -> 675 bytes lib/html/.svn/text-base/boat.gif.svn-base | Bin 0 -> 4347 bytes lib/html/.svn/text-base/boat.png.svn-base | Bin 0 -> 1627 bytes lib/html/.svn/text-base/bottle.jpg.svn-base | Bin 0 -> 1332 bytes lib/html/.svn/text-base/bottle.png.svn-base | Bin 0 -> 4335 bytes .../.svn/text-base/brown_pirate.png.svn-base | Bin 0 -> 612 bytes lib/html/.svn/text-base/cell.png.svn-base | Bin 0 -> 293 bytes .../.svn/text-base/complex.html.netbeans-base | 314 ++ lib/html/.svn/text-base/complex.html.svn-base | 237 ++ lib/html/.svn/text-base/dagger.jpg.svn-base | Bin 0 -> 1396 bytes lib/html/.svn/text-base/dagger.png.svn-base | Bin 0 -> 4590 bytes .../.svn/text-base/green_pirate.png.svn-base | Bin 0 -> 612 bytes lib/html/.svn/text-base/gun.jpg.svn-base | Bin 0 -> 1404 bytes lib/html/.svn/text-base/gun.png.svn-base | Bin 0 -> 4686 bytes lib/html/.svn/text-base/hat.jpg.svn-base | Bin 0 -> 1429 bytes lib/html/.svn/text-base/hat.png.svn-base | Bin 0 -> 4561 bytes .../.svn/text-base/index.html.netbeans-base | 54 + lib/html/.svn/text-base/index.html.svn-base | 54 + .../.svn/text-base/interface.html.svn-base | 123 + lib/html/.svn/text-base/keys.jpg.svn-base | Bin 0 -> 1472 bytes lib/html/.svn/text-base/keys.png.svn-base | Bin 0 -> 4970 bytes .../.svn/text-base/red_pirate.png.svn-base | Bin 0 -> 612 bytes lib/html/.svn/text-base/retreat.png.svn-base | Bin 0 -> 217 bytes lib/html/.svn/text-base/rules.html.svn-base | 53 + .../.svn/text-base/simple.html.netbeans-base | 76 + lib/html/.svn/text-base/simple.html.svn-base | 76 + lib/html/.svn/text-base/skull.jpg.svn-base | Bin 0 -> 1478 bytes lib/html/.svn/text-base/skull.png.svn-base | Bin 0 -> 5069 bytes .../.svn/text-base/yellow_pirate.png.svn-base | Bin 0 -> 612 bytes lib/html/blue-pirate.gif | Bin 0 -> 982 bytes lib/html/blue_pirate.png | Bin 0 -> 675 bytes lib/html/boat.gif | Bin 0 -> 4347 bytes lib/html/boat.png | Bin 0 -> 1627 bytes lib/html/bottle.jpg | Bin 0 -> 1332 bytes lib/html/bottle.png | Bin 0 -> 4335 bytes lib/html/brown_pirate.png | Bin 0 -> 612 bytes lib/html/cell.png | Bin 0 -> 293 bytes lib/html/complex.html | 237 ++ lib/html/dagger.jpg | Bin 0 -> 1396 bytes lib/html/dagger.png | Bin 0 -> 4590 bytes lib/html/green_pirate.png | Bin 0 -> 612 bytes lib/html/gun.jpg | Bin 0 -> 1404 bytes lib/html/gun.png | Bin 0 -> 4686 bytes lib/html/hat.jpg | Bin 0 -> 1429 bytes lib/html/hat.png | Bin 0 -> 4561 bytes lib/html/index.html | 59 + lib/html/interface.html | 123 + lib/html/keys.jpg | Bin 0 -> 1472 bytes lib/html/keys.png | Bin 0 -> 4970 bytes lib/html/red_pirate.png | Bin 0 -> 612 bytes lib/html/retreat.png | Bin 0 -> 217 bytes lib/html/robots/.svn/all-wcprops | 5 + lib/html/robots/.svn/entries | 34 + lib/html/robots/.svn/format | 1 + lib/html/robots/1/.svn/all-wcprops | 11 + lib/html/robots/1/.svn/entries | 27 + lib/html/robots/1/.svn/format | 1 + .../robots/1/.svn/text-base/runme.rb.svn-base | 26 + lib/html/robots/1/runme.rb | 26 + lib/html/robots/2/.svn/all-wcprops | 11 + lib/html/robots/2/.svn/entries | 39 + lib/html/robots/2/.svn/format | 1 + .../robots/2/.svn/text-base/runme.rb.svn-base | 27 + lib/html/robots/2/runme.rb | 27 + lib/html/rules.html | 53 + lib/html/simple.html | 76 + lib/html/skull.jpg | Bin 0 -> 1478 bytes lib/html/skull.png | Bin 0 -> 5069 bytes lib/html/yellow_pirate.png | Bin 0 -> 612 bytes lib/libcartagena.rb | 722 ++++ lib/main.rb | 6 + lib/random-move.rb | 27 + nbproject/.svn/all-wcprops | 17 + nbproject/.svn/dir-prop-base | 6 + nbproject/.svn/entries | 52 + nbproject/.svn/format | 1 + .../text-base/project.properties.svn-base | 4 + nbproject/.svn/text-base/project.xml.svn-base | 15 + nbproject/private/private.xml | 7 + nbproject/private/rake-d.txt | 0 nbproject/project.properties | 4 + nbproject/project.xml | 15 + test-game-play | 135 + test-game-play-to-win | 135 + test-load-file-1 | 53 + test-load-file-2 | 43 + test-load-file-3 | 43 + test/.svn/all-wcprops | 11 + test/.svn/entries | 39 + test/.svn/format | 1 + .../libcartagena_test.rb.netbeans-base | 2065 ++++++++++++ .../text-base/libcartagena_test.rb.svn-base | 2920 +++++++++++++++++ test/libcartagena_test.rb | 2920 +++++++++++++++++ 158 files changed, 19174 insertions(+) create mode 100644 README create mode 100644 Rakefile create mode 100644 game-lines-11.txt create mode 100644 game-lines-12.txt create mode 100644 game-lines-13.txt create mode 100644 game-lines-15.txt create mode 100644 game-lines-16.txt create mode 100644 game-lines-17.txt create mode 100644 game-lines-18.txt create mode 100644 game-lines-19.txt create mode 100644 game-lines-20.txt create mode 100644 game-lines-21.txt create mode 100644 game-lines-8.txt create mode 100644 lib/.LCKlibcartagena.rb~ create mode 100644 lib/.svn/all-wcprops create mode 100644 lib/.svn/entries create mode 100644 lib/.svn/format create mode 100644 lib/.svn/prop-base/first-move.rb.svn-base create mode 100644 lib/.svn/prop-base/random-move.rb.svn-base create mode 100644 lib/.svn/text-base/first-move.rb.svn-base create mode 100644 lib/.svn/text-base/game_handler.rb.svn-base create mode 100644 lib/.svn/text-base/libcartagena.rb.netbeans-base create mode 100644 lib/.svn/text-base/libcartagena.rb.svn-base create mode 100644 lib/.svn/text-base/main.rb.svn-base create mode 100644 lib/.svn/text-base/random-move.rb.netbeans-base create mode 100644 lib/.svn/text-base/random-move.rb.svn-base create mode 100644 lib/.svn/tmp/tempfile.2.tmp create mode 100644 lib/.svn/tmp/tempfile.3.tmp create mode 100644 lib/.svn/tmp/tempfile.4.tmp create mode 100644 lib/.svn/tmp/tempfile.5.tmp create mode 100644 lib/.svn/tmp/tempfile.6.tmp create mode 100644 lib/.svn/tmp/tempfile.tmp create mode 100644 lib/cgi/.svn/all-wcprops create mode 100644 lib/cgi/.svn/entries create mode 100644 lib/cgi/.svn/format create mode 100644 lib/cgi/.svn/text-base/cartagena-simple.rb.svn-base create mode 100755 lib/cgi/cartagena-simple.rb create mode 100755 lib/first-move.rb create mode 100644 lib/game_handler.rb create mode 100644 lib/html/.svn/all-wcprops create mode 100644 lib/html/.svn/entries create mode 100644 lib/html/.svn/format create mode 100644 lib/html/.svn/prop-base/blue-pirate.gif.svn-base create mode 100644 lib/html/.svn/prop-base/blue_pirate.png.svn-base create mode 100644 lib/html/.svn/prop-base/boat.gif.svn-base create mode 100644 lib/html/.svn/prop-base/boat.png.svn-base create mode 100644 lib/html/.svn/prop-base/bottle.jpg.svn-base create mode 100644 lib/html/.svn/prop-base/bottle.png.svn-base create mode 100644 lib/html/.svn/prop-base/brown_pirate.png.svn-base create mode 100644 lib/html/.svn/prop-base/cell.png.svn-base create mode 100644 lib/html/.svn/prop-base/dagger.jpg.svn-base create mode 100644 lib/html/.svn/prop-base/dagger.png.svn-base create mode 100644 lib/html/.svn/prop-base/green_pirate.png.svn-base create mode 100644 lib/html/.svn/prop-base/gun.jpg.svn-base create mode 100644 lib/html/.svn/prop-base/gun.png.svn-base create mode 100644 lib/html/.svn/prop-base/hat.jpg.svn-base create mode 100644 lib/html/.svn/prop-base/hat.png.svn-base create mode 100644 lib/html/.svn/prop-base/keys.jpg.svn-base create mode 100644 lib/html/.svn/prop-base/keys.png.svn-base create mode 100644 lib/html/.svn/prop-base/red_pirate.png.svn-base create mode 100644 lib/html/.svn/prop-base/retreat.png.svn-base create mode 100644 lib/html/.svn/prop-base/skull.jpg.svn-base create mode 100644 lib/html/.svn/prop-base/skull.png.svn-base create mode 100644 lib/html/.svn/prop-base/yellow_pirate.png.svn-base create mode 100644 lib/html/.svn/text-base/blue-pirate.gif.svn-base create mode 100644 lib/html/.svn/text-base/blue_pirate.png.svn-base create mode 100644 lib/html/.svn/text-base/boat.gif.svn-base create mode 100644 lib/html/.svn/text-base/boat.png.svn-base create mode 100644 lib/html/.svn/text-base/bottle.jpg.svn-base create mode 100644 lib/html/.svn/text-base/bottle.png.svn-base create mode 100644 lib/html/.svn/text-base/brown_pirate.png.svn-base create mode 100644 lib/html/.svn/text-base/cell.png.svn-base create mode 100644 lib/html/.svn/text-base/complex.html.netbeans-base create mode 100644 lib/html/.svn/text-base/complex.html.svn-base create mode 100644 lib/html/.svn/text-base/dagger.jpg.svn-base create mode 100644 lib/html/.svn/text-base/dagger.png.svn-base create mode 100644 lib/html/.svn/text-base/green_pirate.png.svn-base create mode 100644 lib/html/.svn/text-base/gun.jpg.svn-base create mode 100644 lib/html/.svn/text-base/gun.png.svn-base create mode 100644 lib/html/.svn/text-base/hat.jpg.svn-base create mode 100644 lib/html/.svn/text-base/hat.png.svn-base create mode 100644 lib/html/.svn/text-base/index.html.netbeans-base create mode 100644 lib/html/.svn/text-base/index.html.svn-base create mode 100644 lib/html/.svn/text-base/interface.html.svn-base create mode 100644 lib/html/.svn/text-base/keys.jpg.svn-base create mode 100644 lib/html/.svn/text-base/keys.png.svn-base create mode 100644 lib/html/.svn/text-base/red_pirate.png.svn-base create mode 100644 lib/html/.svn/text-base/retreat.png.svn-base create mode 100644 lib/html/.svn/text-base/rules.html.svn-base create mode 100644 lib/html/.svn/text-base/simple.html.netbeans-base create mode 100644 lib/html/.svn/text-base/simple.html.svn-base create mode 100644 lib/html/.svn/text-base/skull.jpg.svn-base create mode 100644 lib/html/.svn/text-base/skull.png.svn-base create mode 100644 lib/html/.svn/text-base/yellow_pirate.png.svn-base create mode 100644 lib/html/blue-pirate.gif create mode 100644 lib/html/blue_pirate.png create mode 100644 lib/html/boat.gif create mode 100644 lib/html/boat.png create mode 100644 lib/html/bottle.jpg create mode 100644 lib/html/bottle.png create mode 100644 lib/html/brown_pirate.png create mode 100644 lib/html/cell.png create mode 100644 lib/html/complex.html create mode 100644 lib/html/dagger.jpg create mode 100644 lib/html/dagger.png create mode 100644 lib/html/green_pirate.png create mode 100644 lib/html/gun.jpg create mode 100644 lib/html/gun.png create mode 100644 lib/html/hat.jpg create mode 100644 lib/html/hat.png create mode 100644 lib/html/index.html create mode 100644 lib/html/interface.html create mode 100644 lib/html/keys.jpg create mode 100644 lib/html/keys.png create mode 100644 lib/html/red_pirate.png create mode 100644 lib/html/retreat.png create mode 100644 lib/html/robots/.svn/all-wcprops create mode 100644 lib/html/robots/.svn/entries create mode 100644 lib/html/robots/.svn/format create mode 100644 lib/html/robots/1/.svn/all-wcprops create mode 100644 lib/html/robots/1/.svn/entries create mode 100644 lib/html/robots/1/.svn/format create mode 100644 lib/html/robots/1/.svn/text-base/runme.rb.svn-base create mode 100644 lib/html/robots/1/runme.rb create mode 100644 lib/html/robots/2/.svn/all-wcprops create mode 100644 lib/html/robots/2/.svn/entries create mode 100644 lib/html/robots/2/.svn/format create mode 100644 lib/html/robots/2/.svn/text-base/runme.rb.svn-base create mode 100644 lib/html/robots/2/runme.rb create mode 100644 lib/html/rules.html create mode 100644 lib/html/simple.html create mode 100644 lib/html/skull.jpg create mode 100644 lib/html/skull.png create mode 100644 lib/html/yellow_pirate.png create mode 100644 lib/libcartagena.rb create mode 100644 lib/main.rb create mode 100755 lib/random-move.rb create mode 100644 nbproject/.svn/all-wcprops create mode 100644 nbproject/.svn/dir-prop-base create mode 100644 nbproject/.svn/entries create mode 100644 nbproject/.svn/format create mode 100644 nbproject/.svn/text-base/project.properties.svn-base create mode 100644 nbproject/.svn/text-base/project.xml.svn-base create mode 100644 nbproject/private/private.xml create mode 100644 nbproject/private/rake-d.txt create mode 100644 nbproject/project.properties create mode 100644 nbproject/project.xml create mode 100644 test-game-play create mode 100644 test-game-play-to-win create mode 100644 test-load-file-1 create mode 100644 test-load-file-2 create mode 100644 test-load-file-3 create mode 100644 test/.svn/all-wcprops create mode 100644 test/.svn/entries create mode 100644 test/.svn/format create mode 100644 test/.svn/text-base/libcartagena_test.rb.netbeans-base create mode 100644 test/.svn/text-base/libcartagena_test.rb.svn-base create mode 100644 test/libcartagena_test.rb diff --git a/README b/README new file mode 100644 index 0000000..e91c184 --- /dev/null +++ b/README @@ -0,0 +1,3 @@ +== Cartagena + +You should document your project here. diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..9d6872f --- /dev/null +++ b/Rakefile @@ -0,0 +1,8 @@ +# +# To change this template, choose Tools | Templates +# and open the template in the editor. + + +require 'rake' +require 'rake/testtask' +require 'rake/rdoctask' diff --git a/game-lines-11.txt b/game-lines-11.txt new file mode 100644 index 0000000..6e9b845 --- /dev/null +++ b/game-lines-11.txt @@ -0,0 +1,166 @@ +3 +cell +gun +keys +dagger +hat +skull +bottle +keys +dagger +skull +hat +gun +bottle +dagger +bottle +keys +gun +hat +skull +dagger +skull +bottle +gun +keys +hat +hat +dagger +keys +bottle +gun +skull +keys +bottle +skull +dagger +hat +gun +boat +1 0 1 +1 0 2 +1 2 1 +2 0 2 +2 0 3 +2 3 2 +3 0 3 +3 0 4 +3 4 3 +1 0 4 +1 0 5 +1 5 4 +2 0 5 +2 0 6 +2 6 5 +3 0 6 +3 0 11 +3 11 6 +1 0 7 +1 7 6 +2 0 8 +2 8 5 +3 0 9 +3 9 4 +1 0 10 +1 10 3 +2 0 11 +2 11 2 +3 0 12 +3 12 1 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +1 1 10 +2 2 13 dagger +2 2 14 bottle +2 2 15 keys +3 1 16 gun +3 3 17 hat +3 3 18 skull +1 +hat +skull +bottle +gun +dagger +keys +hat +skull +bottle +gun +dagger +keys +skull +bottle +dagger \ No newline at end of file diff --git a/game-lines-12.txt b/game-lines-12.txt new file mode 100644 index 0000000..3dea36a --- /dev/null +++ b/game-lines-12.txt @@ -0,0 +1,166 @@ +3 +cell +gun +keys +dagger +hat +skull +bottle +keys +dagger +skull +hat +gun +bottle +dagger +bottle +keys +gun +hat +skull +dagger +skull +bottle +gun +keys +hat +hat +dagger +keys +bottle +gun +skull +keys +bottle +skull +dagger +hat +gun +boat +1 0 1 +1 0 2 +1 2 1 +2 0 2 +2 0 3 +2 3 2 +3 0 3 +3 0 4 +3 4 3 +1 0 4 +1 0 5 +1 5 4 +2 0 5 +2 0 6 +2 6 5 +3 0 6 +3 0 11 +3 11 6 +1 0 7 +1 7 6 +2 0 8 +2 8 5 +3 0 9 +3 9 4 +1 0 10 +1 10 3 +2 0 11 +2 11 2 +3 0 12 +3 12 1 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +1 1 10 +2 2 13 dagger +2 2 14 bottle +2 2 15 keys +3 1 16 gun +3 3 17 hat +3 3 18 skull +1 1 24 hat +1 3 25 hat +1 4 35 hat +2 +hat +skull +bottle +gun +dagger +keys +hat +skull +bottle +gun +dagger +keys \ No newline at end of file diff --git a/game-lines-13.txt b/game-lines-13.txt new file mode 100644 index 0000000..3e051b7 --- /dev/null +++ b/game-lines-13.txt @@ -0,0 +1,169 @@ +3 +cell +gun +keys +dagger +hat +skull +bottle +keys +dagger +skull +hat +gun +bottle +dagger +bottle +keys +gun +hat +skull +dagger +skull +bottle +gun +keys +hat +hat +dagger +keys +bottle +gun +skull +keys +bottle +skull +dagger +hat +gun +boat +1 0 1 +1 0 2 +1 2 1 +2 0 2 +2 0 3 +2 3 2 +3 0 3 +3 0 4 +3 4 3 +1 0 4 +1 0 5 +1 5 4 +2 0 5 +2 0 6 +2 6 5 +3 0 6 +3 0 11 +3 11 6 +1 0 7 +1 7 6 +2 0 8 +2 8 5 +3 0 9 +3 9 4 +1 0 10 +1 10 3 +2 0 11 +2 11 2 +3 0 12 +3 12 1 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +1 1 10 +2 2 13 dagger +2 2 14 bottle +2 2 15 keys +3 1 16 gun +3 3 17 hat +3 3 18 skull +1 1 24 hat +1 3 25 hat +1 4 35 hat +2 5 37 hat +2 5 37 hat +2 5 37 hat +3 +skull +skull +bottle +gun +dagger +keys +bottle +skull +bottle +gun +dagger +keys \ No newline at end of file diff --git a/game-lines-15.txt b/game-lines-15.txt new file mode 100644 index 0000000..86663ed --- /dev/null +++ b/game-lines-15.txt @@ -0,0 +1,172 @@ +3 +cell +gun +keys +dagger +hat +skull +bottle +keys +dagger +skull +hat +gun +bottle +dagger +bottle +keys +gun +hat +skull +dagger +skull +bottle +gun +keys +hat +hat +dagger +keys +bottle +gun +skull +keys +bottle +skull +dagger +hat +gun +boat +1 0 1 +1 0 2 +1 2 1 +2 0 2 +2 0 3 +2 3 2 +3 0 3 +3 0 4 +3 4 3 +1 0 4 +1 0 5 +1 5 4 +2 0 5 +2 0 6 +2 6 5 +3 0 6 +3 0 11 +3 11 6 +1 0 7 +1 7 6 +2 0 8 +2 8 5 +3 0 9 +3 9 4 +1 0 10 +1 10 3 +2 0 11 +2 11 2 +3 0 12 +3 12 1 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +1 1 10 +2 2 13 dagger +2 2 14 bottle +2 2 15 keys +3 1 16 gun +3 3 17 hat +3 3 18 skull +1 1 24 hat +1 3 25 hat +1 4 35 hat +2 5 37 hat +2 5 37 hat +2 5 37 hat +3 4 12 bottle +3 6 21 bottle +3 6 28 bottle +1 +skull +skull +bottle +gun +dagger +keys +bottle +skull +bottle +gun +dagger +keys \ No newline at end of file diff --git a/game-lines-16.txt b/game-lines-16.txt new file mode 100644 index 0000000..82f338d --- /dev/null +++ b/game-lines-16.txt @@ -0,0 +1,172 @@ +3 +cell +gun +keys +dagger +hat +skull +bottle +keys +dagger +skull +hat +gun +bottle +dagger +bottle +keys +gun +hat +skull +dagger +skull +bottle +gun +keys +hat +hat +dagger +keys +bottle +gun +skull +keys +bottle +skull +dagger +hat +gun +boat +1 0 1 +1 0 2 +1 2 1 +2 0 2 +2 0 3 +2 3 2 +3 0 3 +3 0 4 +3 4 3 +1 0 4 +1 0 5 +1 5 4 +2 0 5 +2 0 6 +2 6 5 +3 0 6 +3 0 11 +3 11 6 +1 0 7 +1 7 6 +2 0 8 +2 8 5 +3 0 9 +3 9 4 +1 0 10 +1 10 3 +2 0 11 +2 11 2 +3 0 12 +3 12 1 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +1 1 10 +2 2 13 dagger +2 2 14 bottle +2 2 15 keys +3 1 16 gun +3 3 17 hat +3 3 18 skull +1 1 24 hat +1 3 25 hat +1 4 35 hat +2 5 37 hat +2 5 37 hat +2 5 37 hat +3 4 12 bottle +3 6 21 bottle +3 6 28 bottle +1 4 32 bottle +1 6 37 bottle +1 10 37 bottle +2 +skull +skull +bottle +gun +dagger +keys +skull +skull +skull \ No newline at end of file diff --git a/game-lines-17.txt b/game-lines-17.txt new file mode 100644 index 0000000..db8a2bb --- /dev/null +++ b/game-lines-17.txt @@ -0,0 +1,175 @@ +3 +cell +gun +keys +dagger +hat +skull +bottle +keys +dagger +skull +hat +gun +bottle +dagger +bottle +keys +gun +hat +skull +dagger +skull +bottle +gun +keys +hat +hat +dagger +keys +bottle +gun +skull +keys +bottle +skull +dagger +hat +gun +boat +1 0 1 +1 0 2 +1 2 1 +2 0 2 +2 0 3 +2 3 2 +3 0 3 +3 0 4 +3 4 3 +1 0 4 +1 0 5 +1 5 4 +2 0 5 +2 0 6 +2 6 5 +3 0 6 +3 0 11 +3 11 6 +1 0 7 +1 7 6 +2 0 8 +2 8 5 +3 0 9 +3 9 4 +1 0 10 +1 10 3 +2 0 11 +2 11 2 +3 0 12 +3 12 1 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +1 1 10 +2 2 13 dagger +2 2 14 bottle +2 2 15 keys +3 1 16 gun +3 3 17 hat +3 3 18 skull +1 1 24 hat +1 3 25 hat +1 4 35 hat +2 5 37 hat +2 5 37 hat +2 5 37 hat +3 4 12 bottle +3 6 21 bottle +3 6 28 bottle +1 4 32 bottle +1 6 37 bottle +1 10 37 bottle +2 13 37 bottle +2 14 23 keys +2 15 27 keys +3 +skull +skull +keys +gun +dagger +keys +skull +skull +skull \ No newline at end of file diff --git a/game-lines-18.txt b/game-lines-18.txt new file mode 100644 index 0000000..36f4fee --- /dev/null +++ b/game-lines-18.txt @@ -0,0 +1,178 @@ +3 +cell +gun +keys +dagger +hat +skull +bottle +keys +dagger +skull +hat +gun +bottle +dagger +bottle +keys +gun +hat +skull +dagger +skull +bottle +gun +keys +hat +hat +dagger +keys +bottle +gun +skull +keys +bottle +skull +dagger +hat +gun +boat +1 0 1 +1 0 2 +1 2 1 +2 0 2 +2 0 3 +2 3 2 +3 0 3 +3 0 4 +3 4 3 +1 0 4 +1 0 5 +1 5 4 +2 0 5 +2 0 6 +2 6 5 +3 0 6 +3 0 11 +3 11 6 +1 0 7 +1 7 6 +2 0 8 +2 8 5 +3 0 9 +3 9 4 +1 0 10 +1 10 3 +2 0 11 +2 11 2 +3 0 12 +3 12 1 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +1 1 10 +2 2 13 dagger +2 2 14 bottle +2 2 15 keys +3 1 16 gun +3 3 17 hat +3 3 18 skull +1 1 24 hat +1 3 25 hat +1 4 35 hat +2 5 37 hat +2 5 37 hat +2 5 37 hat +3 4 12 bottle +3 6 21 bottle +3 6 28 bottle +1 4 32 bottle +1 6 37 bottle +1 10 37 bottle +2 13 37 bottle +2 14 23 keys +2 15 27 keys +3 12 20 skull +3 16 31 keys +3 17 37 keys +1 +skull +skull +keys +gun +dagger +keys +skull +skull +skull \ No newline at end of file diff --git a/game-lines-19.txt b/game-lines-19.txt new file mode 100644 index 0000000..e0ef244 --- /dev/null +++ b/game-lines-19.txt @@ -0,0 +1,178 @@ +3 +cell +gun +keys +dagger +hat +skull +bottle +keys +dagger +skull +hat +gun +bottle +dagger +bottle +keys +gun +hat +skull +dagger +skull +bottle +gun +keys +hat +hat +dagger +keys +bottle +gun +skull +keys +bottle +skull +dagger +hat +gun +boat +1 0 1 +1 0 2 +1 2 1 +2 0 2 +2 0 3 +2 3 2 +3 0 3 +3 0 4 +3 4 3 +1 0 4 +1 0 5 +1 5 4 +2 0 5 +2 0 6 +2 6 5 +3 0 6 +3 0 11 +3 11 6 +1 0 7 +1 7 6 +2 0 8 +2 8 5 +3 0 9 +3 9 4 +1 0 10 +1 10 3 +2 0 11 +2 11 2 +3 0 12 +3 12 1 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +1 1 10 +2 2 13 dagger +2 2 14 bottle +2 2 15 keys +3 1 16 gun +3 3 17 hat +3 3 18 skull +1 1 24 hat +1 3 25 hat +1 4 35 hat +2 5 37 hat +2 5 37 hat +2 5 37 hat +3 4 12 bottle +3 6 21 bottle +3 6 28 bottle +1 4 32 bottle +1 6 37 bottle +1 10 37 bottle +2 13 37 bottle +2 14 23 keys +2 15 27 keys +3 12 20 skull +3 16 31 keys +3 17 37 keys +1 24 37 keys +1 25 37 keys +1 32 36 gun +2 +skull +skull +gun +gun +dagger +gun \ No newline at end of file diff --git a/game-lines-20.txt b/game-lines-20.txt new file mode 100644 index 0000000..a47098b --- /dev/null +++ b/game-lines-20.txt @@ -0,0 +1,181 @@ +3 +cell +gun +keys +dagger +hat +skull +bottle +keys +dagger +skull +hat +gun +bottle +dagger +bottle +keys +gun +hat +skull +dagger +skull +bottle +gun +keys +hat +hat +dagger +keys +bottle +gun +skull +keys +bottle +skull +dagger +hat +gun +boat +1 0 1 +1 0 2 +1 2 1 +2 0 2 +2 0 3 +2 3 2 +3 0 3 +3 0 4 +3 4 3 +1 0 4 +1 0 5 +1 5 4 +2 0 5 +2 0 6 +2 6 5 +3 0 6 +3 0 11 +3 11 6 +1 0 7 +1 7 6 +2 0 8 +2 8 5 +3 0 9 +3 9 4 +1 0 10 +1 10 3 +2 0 11 +2 11 2 +3 0 12 +3 12 1 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +1 1 10 +2 2 13 dagger +2 2 14 bottle +2 2 15 keys +3 1 16 gun +3 3 17 hat +3 3 18 skull +1 1 24 hat +1 3 25 hat +1 4 35 hat +2 5 37 hat +2 5 37 hat +2 5 37 hat +3 4 12 bottle +3 6 21 bottle +3 6 28 bottle +1 4 32 bottle +1 6 37 bottle +1 10 37 bottle +2 13 37 bottle +2 14 23 keys +2 15 27 keys +3 12 20 skull +3 16 31 keys +3 17 37 keys +1 24 37 keys +1 25 37 keys +1 32 36 gun +2 23 30 skull +2 27 33 skull +2 30 37 skull +3 +skull +skull +gun +gun +dagger +gun \ No newline at end of file diff --git a/game-lines-21.txt b/game-lines-21.txt new file mode 100644 index 0000000..f56f978 --- /dev/null +++ b/game-lines-21.txt @@ -0,0 +1,184 @@ +3 +cell +gun +keys +dagger +hat +skull +bottle +keys +dagger +skull +hat +gun +bottle +dagger +bottle +keys +gun +hat +skull +dagger +skull +bottle +gun +keys +hat +hat +dagger +keys +bottle +gun +skull +keys +bottle +skull +dagger +hat +gun +boat +1 0 1 +1 0 2 +1 2 1 +2 0 2 +2 0 3 +2 3 2 +3 0 3 +3 0 4 +3 4 3 +1 0 4 +1 0 5 +1 5 4 +2 0 5 +2 0 6 +2 6 5 +3 0 6 +3 0 11 +3 11 6 +1 0 7 +1 7 6 +2 0 8 +2 8 5 +3 0 9 +3 9 4 +1 0 10 +1 10 3 +2 0 11 +2 11 2 +3 0 12 +3 12 1 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +1 1 10 +2 2 13 dagger +2 2 14 bottle +2 2 15 keys +3 1 16 gun +3 3 17 hat +3 3 18 skull +1 1 24 hat +1 3 25 hat +1 4 35 hat +2 5 37 hat +2 5 37 hat +2 5 37 hat +3 4 12 bottle +3 6 21 bottle +3 6 28 bottle +1 4 32 bottle +1 6 37 bottle +1 10 37 bottle +2 13 37 bottle +2 14 23 keys +2 15 27 keys +3 12 20 skull +3 16 31 keys +3 17 37 keys +1 24 37 keys +1 25 37 keys +1 32 36 gun +2 23 30 skull +2 27 33 skull +2 30 37 skull +3 18 30 skull +3 20 37 skull +3 21 37 skull +1 +gun +gun +gun +gun +gun +gun \ No newline at end of file diff --git a/game-lines-8.txt b/game-lines-8.txt new file mode 100644 index 0000000..4750066 --- /dev/null +++ b/game-lines-8.txt @@ -0,0 +1,160 @@ +3 +cell +gun +keys +dagger +hat +skull +bottle +keys +dagger +skull +hat +gun +bottle +dagger +bottle +keys +gun +hat +skull +dagger +skull +bottle +gun +keys +hat +hat +dagger +keys +bottle +gun +skull +keys +bottle +skull +dagger +hat +gun +boat +1 0 1 +1 0 2 +1 2 1 +2 0 2 +2 0 3 +2 3 2 +3 0 3 +3 0 4 +3 4 3 +1 0 4 +1 0 5 +1 5 4 +2 0 5 +2 0 6 +2 6 5 +3 0 6 +3 0 11 +3 11 6 +1 0 7 +1 7 6 +2 0 8 +2 8 5 +3 0 9 +3 9 4 +1 0 10 +1 10 3 +2 0 11 +2 11 2 +3 0 12 +3 12 1 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +2 2 8 +2 8 2 +3 3 9 +3 9 3 +1 1 10 +1 10 1 +2 2 11 +2 11 2 +3 3 12 +3 12 3 +1 1 7 +1 7 1 +1 1 10 +1 +hat +skull +bottle +gun +dagger +keys +hat +skull +bottle +gun +dagger +keys +skull +bottle +dagger \ No newline at end of file diff --git a/lib/.LCKlibcartagena.rb~ b/lib/.LCKlibcartagena.rb~ new file mode 100644 index 0000000..9922eec --- /dev/null +++ b/lib/.LCKlibcartagena.rb~ @@ -0,0 +1 @@ +/home/neil/programming/ruby/Cartagena/lib/.LCKlibcartagena.rb~ \ No newline at end of file diff --git a/lib/.svn/all-wcprops b/lib/.svn/all-wcprops new file mode 100644 index 0000000..3641f48 --- /dev/null +++ b/lib/.svn/all-wcprops @@ -0,0 +1,35 @@ +K 25 +svn:wc:ra_dav:version-url +V 41 +/svn/njae/!svn/ver/58/cartagena/trunk/lib +END +main.rb +K 25 +svn:wc:ra_dav:version-url +V 49 +/svn/njae/!svn/ver/25/cartagena/trunk/lib/main.rb +END +random-move.rb +K 25 +svn:wc:ra_dav:version-url +V 56 +/svn/njae/!svn/ver/43/cartagena/trunk/lib/random-move.rb +END +game_handler.rb +K 25 +svn:wc:ra_dav:version-url +V 57 +/svn/njae/!svn/ver/42/cartagena/trunk/lib/game_handler.rb +END +first-move.rb +K 25 +svn:wc:ra_dav:version-url +V 55 +/svn/njae/!svn/ver/42/cartagena/trunk/lib/first-move.rb +END +libcartagena.rb +K 25 +svn:wc:ra_dav:version-url +V 57 +/svn/njae/!svn/ver/56/cartagena/trunk/lib/libcartagena.rb +END diff --git a/lib/.svn/entries b/lib/.svn/entries new file mode 100644 index 0000000..0917298 --- /dev/null +++ b/lib/.svn/entries @@ -0,0 +1,95 @@ +8 + +dir +65 +http://scripts.njae.me.uk/svn/njae/cartagena/trunk/lib +http://scripts.njae.me.uk/svn/njae + + + +2008-12-08T21:03:51.254104Z +58 +neil + + +svn:special svn:externals svn:needs-lock + + + + + + + + + + + +0ada9808-85ab-4924-adda-5bf89eae3818 + +cgi +dir + +html +dir + +main.rb +file + + + + +2008-08-19T15:48:21.000000Z +6c2cf56248662af6cff4847c8912f4c6 +2008-06-12T10:24:34.892518Z +25 +neil + +random-move.rb +file + + + + +2008-10-17T07:27:48.000000Z +9d7aa6b67c047dd18ff05819c78e9a7d +2008-10-17T07:57:36.358504Z +43 + +has-props + +game_handler.rb +file + + + + +2008-10-16T13:08:44.000000Z +fe6d99bbf7aa3c4330755efa61255308 +2008-10-16T20:38:07.769887Z +42 + +first-move.rb +file + + + + +2008-10-16T13:19:45.000000Z +db3d894459b82c2f69b1ddd6b5d66858 +2008-10-16T20:38:07.769887Z +42 + +has-props + +libcartagena.rb +file + + + + +2008-12-21T01:02:45.000000Z +dfafcb99a9164f1b85f78bd7d48d2340 +2008-12-05T21:50:45.570571Z +56 +neil + diff --git a/lib/.svn/format b/lib/.svn/format new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/lib/.svn/format @@ -0,0 +1 @@ +8 diff --git a/lib/.svn/prop-base/first-move.rb.svn-base b/lib/.svn/prop-base/first-move.rb.svn-base new file mode 100644 index 0000000..869ac71 --- /dev/null +++ b/lib/.svn/prop-base/first-move.rb.svn-base @@ -0,0 +1,5 @@ +K 14 +svn:executable +V 1 +* +END diff --git a/lib/.svn/prop-base/random-move.rb.svn-base b/lib/.svn/prop-base/random-move.rb.svn-base new file mode 100644 index 0000000..869ac71 --- /dev/null +++ b/lib/.svn/prop-base/random-move.rb.svn-base @@ -0,0 +1,5 @@ +K 14 +svn:executable +V 1 +* +END diff --git a/lib/.svn/text-base/first-move.rb.svn-base b/lib/.svn/text-base/first-move.rb.svn-base new file mode 100644 index 0000000..4c96457 --- /dev/null +++ b/lib/.svn/text-base/first-move.rb.svn-base @@ -0,0 +1,26 @@ +#!/usr/bin/ruby +# +# == Synopsis +# +# Play one move of a Cartagena game +# +# == Usage +# clockwise-p1 +# Game state file read on STDIN +# Move produced on STDOUT +# +# == Author +# Neil Smith + +# +# To change this template, choose Tools | Templates +# and open the template in the editor. + + +require 'libcartagena' +require 'rdoc/usage' + +gamelines = readlines +game, moves = Game.read_game(gamelines) +move = game.possible_moves[0] +puts move.format(game.board, true) diff --git a/lib/.svn/text-base/game_handler.rb.svn-base b/lib/.svn/text-base/game_handler.rb.svn-base new file mode 100644 index 0000000..cc8eda7 --- /dev/null +++ b/lib/.svn/text-base/game_handler.rb.svn-base @@ -0,0 +1,59 @@ +# == Synopsis +# +# Play a game of Cartagena. +# +# == Author +# Neil Smith +# +# == Change history +# Version 1.1:: 23 April 2008 + +require 'lib/libcartagena' + +# Play a game to completion, given a set of player agents. +class GameHandler + + attr_reader :game + + # Create a game handler that uses a set of players + def initialize(players, game_length_limit = 5000, verbose = false, very_verbose = false) + @game = Game.new(players.length) + @verbose = verbose + @very_verbose = very_verbose + @game_length_limit = game_length_limit + + # Give each player a name + @named_players = Hash.new + (@game.players.zip players).each do |kv| + @named_players[kv[0]] = kv[1] + end + end + + # Play a game of Cartagena. If players make illegal moves, disqualify them and restart the game. + # Terminate the game if there's a winner, there's only one player left, + # or the game has gone on too long. + def play + while @game.history.length < @game_length_limit + move_to_apply = @named_players[@game.current_player].best_move(@game, roll) + puts "Move #{@game.history.length + 1}: Player #{@game.current_player} rolled #{roll}: making move #{move_to_apply}" if @verbose + @game.apply_moves! [move_to_apply] + puts @game if @very_verbose + end + puts "Game terminated after #{@game.history.length} moves" if @verbose + [:draw, @limit] + rescue GameWonNotice => win_notification + winner = win_notification.message[-1,1] + puts "Game won by #{winner} in #{@game.history.length} moves" if @verbose + [@named_players[winner], @game.history.length] + rescue InvalidMoveError + puts "Disqualifying player #{@game.current_player}" if @verbose + @named_players.delete @game.current_player + if @named_players.length > 1 + retry + else + puts "Game won by #{@named_players.keys[0]} by default" if @verbose + [@named_players[@named_players.keys[0]], 0] + end + end +end + \ No newline at end of file diff --git a/lib/.svn/text-base/libcartagena.rb.netbeans-base b/lib/.svn/text-base/libcartagena.rb.netbeans-base new file mode 100644 index 0000000..f1062bf --- /dev/null +++ b/lib/.svn/text-base/libcartagena.rb.netbeans-base @@ -0,0 +1,726 @@ +# == Synopsis +# +# Library to support Cartagena play +# +# == Author +# Neil Smith +# +# 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 . +# +# == Change history +# Version 1.0:: 11 Jun 2008 +# * Initial build + +# Symbols +$SYMBOLS = [:bottle, :dagger, :gun, :hat, :keys, :skull] + +# The number of cards of each symbol in the deck +$CARDS_PER_SYMBOL = 17 + +# The number of cards initiall dealt to each player +$INITIAL_CARDS_PER_PLAYER = 3 + +# Maximum number of pieces on a position +$MAX_PIECES_PER_POSITION = 3 + +# Number of actions that can be taken by each player in sequence +$MAX_MOVES_PER_TURN = 3 + +# Errors for a game + +# Moves can only be [1..6] spaces +class InvalidMoveError < StandardError +end + +# Game is won when only one player has uncaptured pieces +class GameWonNotice < StandardError +end + +# A position on the board. +class Position + attr_accessor :symbol + attr_accessor :contains + + def initialize(symbol) + @symbol = symbol + @contains = [] + end +end + +# A tile that makes up the board. Each tile has two sides, each with six +# positions. Tiles can be either way up, and either way round. +class Tile + attr_reader :exposed, :front, :back + + def initialize(front, back) + @front = front.collect {|s| Position.new(s)} + @back = back.collect {|s| Position.new(s)} + @exposed = @front + @exposed_side = :front + end + + def flip! + if @exposed_side == :front + @exposed = @back + @exposed_side = :back + else + @exposed = @front + @exposed_side = :front + end + end + + def reverse! + @exposed = @exposed.reverse + end +end + + +# The game board +class Board + + attr_accessor :positions + attr_reader :tiles + + # A laborious procedure to create all the positions and tie them all together + def initialize(tiles_used) + # A hash of all positions, indexed by position names + @tiles = [Tile.new([:hat, :keys, :gun, :bottle, :skull, :dagger], + [:hat, :keys, :gun, :bottle, :skull, :dagger]), + Tile.new([:gun, :hat, :dagger, :skull, :bottle, :keys], + [:gun, :hat, :dagger, :skull, :bottle, :keys]), + Tile.new([:skull, :gun, :bottle, :keys, :dagger, :hat], + [:skull, :gun, :bottle, :keys, :dagger, :hat]), + Tile.new([:dagger, :bottle, :keys, :gun, :hat, :skull], + [:dagger, :bottle, :keys, :gun, :hat, :skull]), + Tile.new([:keys, :dagger, :skull, :hat, :gun, :bottle], + [:keys, :dagger, :skull, :hat, :gun, :bottle]), + Tile.new([:bottle, :skull, :hat, :dagger, :keys, :gun], + [:bottle, :skull, :hat, :dagger, :keys, :gun]) + ].shuffle[0...tiles_used] + @tiles = @tiles.each do |t| + if rand < 0.5 + t.reverse! + else + t + end + end + + @positions = [Position.new(:cell)] + @tiles.each {|t| t.exposed.each {|p| @positions << p}} + @positions << Position.new(:boat) + end # def + + def to_s + layout + end + + def to_str + to_s + end + + + # For each position, show its name and what it touches + def layout + out_string = "" + @positions.each {|position| out_string << "#{position.symbol}\n"} + out_string + end + +end + + +# Each piece on the board is an object +class Piece + attr_reader :player, :number + attr_accessor :position + + def initialize(position, player, number) + @position = position + @player = player + @number = number + end + + def to_s + "#{@player}:#{@number}" + end + + def to_str + to_s + end + + def show(convert_to_zero_based_player_number = false) + if convert_to_zero_based_player_number + "#{@player + 1}:#{@number}" + else + "#{@player}:#{@number}" + end + end + + def move_to(new_position) + @position = new_position + end + +end + + +# A move in a game +class Move + attr_reader :piece, :origin, :destination, :card_played + + def initialize(piece, origin, destination, card_played = :unspecified) + @piece = piece + @origin = origin + @destination = destination + @card_played = card_played + end + + def show(board, convert_to_zero_based_player_number = false) + if @card_played == :unspecified + "#{@piece.show(convert_to_zero_based_player_number)}: #{board.positions.index(@origin)} -> #{board.positions.index(@destination)}" + else + "#{@piece.show(convert_to_zero_based_player_number)}: #{board.positions.index(@origin)} -> #{board.positions.index(@destination)} (#{@card_played})" + end + end + + def format(board, convert_to_zero_based_player_number = false) + display_player_number = if convert_to_zero_based_player_number then + @piece.player + 1 + else + @piece.player + end + if @card_played ==:unspecified + "#{display_player_number} #{board.positions.index(@origin)} #{board.positions.index(@destination)}" + else + "#{display_player_number} #{board.positions.index(@origin)} #{board.positions.index(@destination)} #{@card_played}" + end + end + + # Write a move to a string + # Note the inverse, String#to_move, is defined below + def to_s + "#{@piece.player} #{@origin.to_s} #{@destination.to_s} #{@card_played}" + end + + def to_str + to_s + end + +end + + + +# A class to record each of the states previously found in a game. +# Note that this is a deep copy of the pieces and what they've captured, so +# you'll need to convert back +class GameState + attr_accessor :move, :player, :board, :pieces_before_move, :deck_before_move, + :players_cards_before_move, :moves_by_current_player + # this_game_state = GameState.new(move, player, @board, @pieces, @deck, @players_cards) + + + def initialize(move, player, board, pieces, deck, players_cards, moves_by_current_player) + @move, @player, @board, @pieces_before_move, @deck_before_move, + @players_cards_before_move, @moves_by_current_player = + copy_game_state(move, player, board, pieces, deck, players_cards, + moves_by_current_player) + end + + # def ==(other) + # @move.to_s == other.move.to_s and + # @player == other.player and + # @piece_after_move == other.pieces_after_move + # end + + def copy_game_state(move, player, board, pieces, deck, players_cards, moves_by_current_player) + copy_player = player + moving_player = move.piece.player + + copy_board = board.dup + copy_board.positions = board.positions.collect {|pos| pos.dup} + copy_board.positions.each {|pos| pos.contains = []} + + copy_pieces = pieces.collect do |players_pieces| + players_pieces.collect do |piece| + new_piece_position = copy_board.positions[board.positions.index(piece.position)] + new_piece = Piece.new(new_piece_position, piece.player, piece.number) + new_piece_position.contains << new_piece + new_piece + end + end + + piece_index = pieces[moving_player].index(move.piece) + origin_index = board.positions.index(move.origin) + destination_index = board.positions.index(move.destination) + copy_move = Move.new(copy_pieces[moving_player][piece_index], + copy_board.positions[origin_index], + copy_board.positions[destination_index]) + + copy_deck = deck.dup + copy_players_cards = players_cards.collect {|p| p.dup} + copy_moves_by_current_player = moves_by_current_player + + return copy_move, copy_player, copy_board, copy_pieces, copy_deck, copy_players_cards, copy_moves_by_current_player + end + +end + + +# A game of Cartagena. It keeps a history of all previous states. +class Game + + attr_reader :history + attr_accessor :current_player + attr_reader :players + attr_accessor :players_cards + attr_accessor :moves_by_current_player + attr_reader :board + attr_reader :pieces + attr_reader :cards + attr_accessor :deck + + # Create a new game + def initialize(players = 5, number_of_tiles = 6, pieces_each = 6) + @board = Board.new(number_of_tiles) + @history = [] + @pieces = [] + @players = [].fill(0, players) {|i| i} + @players_cards = [] + @current_player = 0 + @moves_by_current_player = 0 + @cards = [] + 1.upto($CARDS_PER_SYMBOL) {|x| @cards.concat($SYMBOLS)} + @deck = @cards.shuffle + @players.each do |p| + @pieces[p] = [] + 0.upto(pieces_each - 1) do |count| + piece = Piece.new(@board.positions[0], p, count) + @pieces[p][count] = piece + @board.positions[0].contains << piece + end + @players_cards[p] = [] + deal_cards!($INITIAL_CARDS_PER_PLAYER, p) + end + end + + # Deal some cards to a player. Remove them from the deck. Refill the deck + # if necessary + def deal_cards!(number_of_cards, player = @current_player) + 1.upto(number_of_cards) do + if @deck.empty? + @deck = @cards + @players_cards.each do |p| + p.each do |c| + @deck.delete_at(@deck.index(c)) + end + end + @deck = @deck.shuffle + end + @players_cards[player] << @deck.pop + end + end + + # Check that a move is valid. Throw an exception if it's invalid + def validate_move(move, player = @current_player) + # Check the move is a valid one + raise(InvalidMoveError, "Move #{move}: Player #{player} does not exist") unless @players.include?(player) + raise(InvalidMoveError, "Move #{move}: None of player #{player}'s pieces on position #{move.origin}") unless move.origin.contains.find {|pc| pc.player == player} + raise(InvalidMoveError, "Move #{move}: Origin and destination are the same") if move.origin == move.destination + + origin_position = @board.positions.index(move.origin) + destination_position = @board.positions.index(move.destination) + # Is this move an advance or a retreat? + if destination_position > origin_position + # Advancing a piece + if move.destination == @board.positions[-1] # A move into the boat + raise(InvalidMoveError, "Move #{move}: Move into boat and card unspecified") if move.destination == @board.positions[-1] and move.card_played == :unspecified + unless @players_cards[player].find {|c| c == move.card_played} + raise(InvalidMoveError, "Player #{player} does not have a card to move a piece into the boat") + end + else + if move.card_played != :unspecified and move.destination.symbol != move.card_played + raise(InvalidMoveError, "Player #{player} trying to move to #{move.destination}, a #{move.destination.symbol} square with a a #{move.card_played} card") + end + unless @players_cards[player].find {|c| c == move.destination.symbol} + raise(InvalidMoveError, "Player #{player} does not have a card to move a piece to a #{move.destination.symbol} square") + end + # Check target square is vacant + raise(InvalidMoveError, "Advance move #{move}: destination occupied") unless move.destination.contains.empty? + end + # Check all the intervening squares with this symbol are occupied + intervening_empty_position = @board.positions[origin_position...destination_position].index_find do |p| + p.symbol == move.destination.symbol and + p.contains.empty? + end + raise(InvalidMoveError, "Advance move #{move}: location #{intervening_empty_position} is empty") if intervening_empty_position + else + # Retreating a piece + # Check target position has one or two pieces already on it + destination_count = move.destination.contains.length + raise(InvalidMoveError, "Retreat move #{move}: destination has no pieces already on it") if destination_count == 0 + raise(InvalidMoveError, "Retreat move #{move}: destination has too many (#{destination_count}) pieces already on it") if destination_count >= $MAX_PIECES_PER_POSITION + # Check none of the intervening squares have any pieces on them + # puts "Checking positions #{destination_position} to #{origin_position}" + intervening_target_position = @board.positions[(destination_position + 1)...origin_position].index_find do |p| + # puts "Examining postition #{p} at location #{@board.positions.index(p)} which contains #{p.contains.length} pieces" + p.contains.length > 0 and + p.contains.length < $MAX_PIECES_PER_POSITION + end + raise(InvalidMoveError, "Retreat move #{move.show(@board)}: location #{intervening_target_position} is a viable target") if intervening_target_position + end + end + + # Apply a single move to a game. + def apply_move!(move, player = @current_player, validate = true) + + validate_move(move, player) if validate + + raise(InvalidMoveError, "Too many consecutive moves by #{@current_player}: has taken #{@moves_by_current_player} and validate is #{validate}") if validate and player == @current_player and @moves_by_current_player >= $MAX_MOVES_PER_TURN + + # Record the old state + this_game_state = GameState.new(move, @current_player, @board, @pieces, @deck, @players_cards, @moves_by_current_player) + @history << this_game_state + + # Apply this move + move.origin.contains.delete move.piece + move.destination.contains << move.piece + move.piece.position = move.destination + + if player == @current_player + @moves_by_current_player += 1 + else + @current_player = player + @moves_by_current_player = 1 + end + + # Update cards + if @board.positions.index(move.destination) > @board.positions.index(move.origin) + # Advance move + if validate + card_to_remove = if move.card_played != :unspecified + move.card_played + else + move.destination.symbol + end + if @players_cards[player].include?(card_to_remove) + @players_cards[player].delete_at(@players_cards[player].index(card_to_remove)) + end + end + else + # Retreat move + deal_cards!(move.destination.contains.length - 1, player) + end + + # If this player has all their pieces in the boat, declare a win. + if @pieces[player].all? {|pc| pc.position == @board.positions[-1]} + raise(GameWonNotice, "Game won by #{player}") + end + end + + # Undo a move + def undo_move! + state_to_restore = @history[-1] + move, @current_player, @board, @pieces, @deck, @players_cards, + @moves_by_current_player = + state_to_restore.copy_game_state(state_to_restore.move, + state_to_restore.player, + state_to_restore.board, + state_to_restore.pieces_before_move, + state_to_restore.deck_before_move, + state_to_restore.players_cards_before_move, + state_to_restore.moves_by_current_player) + @history.pop + end + + # Apply a list of moves in order + def apply_moves!(moves) + moves.each do |move| + @current_player = move.piece.player + apply_move!(move, @current_player) + next_player! if @moves_by_current_player >= $MOVES_PER_TURN + end + end + + + # Set the current player to be the next player + def next_player! + if @current_player == @players[-1] + @current_player = @players[0] + else + @current_player = @players[@players.index(@current_player) + 1] + end + @moves_by_current_player = 0 + @current_player + end + + + def reset_current_player(new_current_player) + @current_player = new_current_player + @moves_by_current_player = 0 + end + + + # Return an array of all possible moves from this state, given the active player + def possible_moves(player = @current_player) + moves = [] + @pieces[player].each do |piece| + # Do a forward move for each card held + unless piece.position == @board.positions[-1] + @players_cards[player].each do |card| + destination = @board.positions[@board.positions.index(piece.position)..-1].find do |pos| + (pos.symbol == card and pos.contains == []) or + pos.symbol == :boat + end + # puts "Player #{player}, card #{card}, piece #{piece.number} at position #{@board.positions.index(piece.position)} to #{@board.positions.index(destination)}, a #{destination.symbol}" + moves << Move.new(piece, piece.position, destination, card) + end + end + # Do a reverse move for the piece + unless piece.position == board.positions[0] + destination = @board.positions[1...@board.positions.index(piece.position)].reverse.find do |pos| + pos.contains.length == 1 or pos.contains.length == 2 + end + if destination + # puts "Player #{player}, piece #{piece.number} at position #{@board.positions.index(piece.position)} retreats to #{@board.positions.index(destination)}, a #{destination.symbol} containing #{destination.contains.length} pieces" + moves << Move.new(piece, piece.position, destination) + end + end + end + # moves.each {|m| puts m.show(@board)} + moves + end + + def build_state_string + outstr = "Current player = #{@current_player}\n" + 0.upto((@board.positions.length)-1) do |i| + outstr << "#{i}: #{@board.positions[i].symbol}: " + @board.positions[i].contains.each do |piece| + outstr << "P#{piece.player}:#{piece.number} " + end + outstr << "\n" + end + 0.upto((@players.length)-1) do |i| + outstr << "Player #{i} holds " << (@players_cards[i].sort_by {|c| c.to_s}).join(', ') << "\n" + end + outstr << "Deck holds " << @deck.join(', ') << "\n" + outstr + end + + # Show the state of the board + def show_state + puts build_state_string + # @pieces.keys.sort.each do |piece_name| + # if @pieces[piece_name].captured + # puts "Piece #{piece_name} captured, at #{@pieces[piece_name].position}" + # else + # puts "Piece #{piece_name} is at #{@pieces[piece_name].position}, holds #{(@pieces[piece_name].contains.collect{|c| c.name}).join(' ')}" + # end + # end + end + + def to_s + show_state + end + + def to_str + to_s + end + + + def set_testing_game! + srand 1234 + @board = nil + initialize(5, 6, 6) + board_symbols = [:cell, :gun, :keys, :dagger, :hat, :skull, :bottle, + :keys, :dagger, :skull, :hat, :gun, :bottle, + :dagger, :bottle, :keys, :gun, :hat, :skull, + :dagger, :skull, :bottle, :gun, :keys, :hat, + :hat, :dagger, :keys, :bottle, :gun, :skull, + :keys, :bottle, :skull, :dagger, :hat, :gun, :boat] + board_symbols.each_index do |i| + @board.positions[i].symbol = board_symbols[i] + end + + @players_cards[0] = [:gun, :hat, :skull] + @players_cards[1] = [:bottle, :keys, :keys] + @players_cards[2] = [:bottle, :hat, :keys] + @players_cards[3] = [:bottle, :skull, :skull] + @players_cards[4] = [:bottle, :gun, :gun] + + @deck = [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle, :skull] + + end + + # Given a set of lines from an input file, turn them into a Game object and + # a set of Move objects. + # Note the multiple return values. + # Class method + def Game.read_game(gamelines) + gamelines.each {|l| l.chomp!} +# puts "Creating game with #{gamelines[0].to_i} players" + game = Game.new(gamelines[0].to_i, 6, 6) + + # create the board + 2.upto(38) do |i| + game.board.positions[i-1] = Position.new(gamelines[i].to_sym) +# puts "Making board position #{i-1} a #{gamelines[i].to_sym}" + end + + # read all the moves so far + moves = [] + i = 39 + current_player_moves = 0 + current_player = 0 + player_card_counts = game.players_cards.map {|p| $INITIAL_CARDS_PER_PLAYER} + unused_cards = game.cards.map {|c| c} + while gamelines[i].in_move_format? + move = gamelines[i].to_move(game, true) +# puts "Applying move #{move.show(game.board)} from line #{i} of the file. Current player before move application is #{game.current_player}" + if move.piece.player == current_player + current_player_moves += 1 + else + current_player = move.piece.player + current_player_moves = 1 + end + # if move is an advance move, decrement the cards held by the player and remove the card from unused cards + # throw an error if an advance move is made when all the cards of that type have been used + # if unused_cards becomes empty, restock it. + if game.board.positions.index(move.destination) > game.board.positions.index(move.origin) + raise(InvalidMoveError, "Game reading: Player #{move.piece.player} attempting an advance move with no cards in hand. Move is #{gamelines[i]} on line #{i}") if player_card_counts[move.piece.player] == 0 + raise(InvalidMoveError, "Game reading: Player #{move.piece.player} attempting an advance move to a #{move.destination.symbol} place when all those cards have been used. Move is #{gamelines[i]} on line #{i}") unless unused_cards.include? move.destination.symbol + player_card_counts[current_player] -= 1 + unused_cards.delete_at(unused_cards.index(move.destination.symbol)) + # if move is a retreat move, increment the cards held by the player + else + player_card_counts[current_player] += move.destination.contains.length + if unused_cards.length == player_card_counts.inject {|sum, n| sum + n } + unused_cards = game.cards.map {|c| c} + end + end + game.apply_move!(move, move.piece.player, false) + moves << move +# puts "Applied move #{move.show(game.board)} from line #{i} of the file" + i = i + 1 + end + + # note the current player + game.current_player = gamelines[i].to_i - 1 + if game.current_player == current_player + game.moves_by_current_player = current_player_moves + else + game.moves_by_current_player = 0 + end +# puts "Setting current player to #{game.current_player} on line #{i}" + current_player = game.current_player + + # read the cards + game.players_cards = game.players_cards.map {|c| []} +# player_card_counts.each_index {|index| puts "#{index} should have #{player_card_counts[index]} cards"} +# game.players_cards.each_index {|index| puts "#{index} has #{game.players_cards[index]} cards"} +# puts "Current player is #{game.current_player} whose cards are #{game.players_cards[game.current_player]}" + (i+1).upto(gamelines.length - 1) do |j| + game.players_cards[game.current_player] << gamelines[j].to_sym + unused_cards.delete_at(unused_cards.index(gamelines[j].to_sym)) +# puts "Reading line #{j} to give player #{game.current_player} a #{gamelines[j].to_sym} card" + end + raise(InvalidMoveError, "Player #{game.current_player} given #{game.players_cards[game.current_player].length} cards, but should have #{player_card_counts[game.current_player]} cards from play") if game.players_cards[game.current_player].length != player_card_counts[game.current_player] + + # Update the game deck + +puts "#{unused_cards.length} unused cards" +player_card_counts.each {|c| puts "has #{c} cards"} + game.deck = unused_cards.shuffle + player_card_counts[game.current_player] = 0 + player_card_counts.each_index do |player| + if player_card_counts[player] > 0 + game.deal_cards!(player_card_counts[player], player) + end + end +puts "#{game.deck.length} cards remaining in deck" +game.players_cards.each {|cards| puts "holds #{cards}"} + return game, moves + end + +end + + +# Extension to String class to convert a move-description string into a Move object. +# This is the inverse of the Move#to_s method +class String + def in_move_format? + elements = split + return ((elements.length == 3 or elements.length == 4) and + elements[0].to_i > 0 and elements[0].to_i < 6 and + elements[1].to_i >= 0 and elements[1].to_i <= 37 and + elements[2].to_i >= 0 and elements[2].to_i <= 37) + end + + def to_move(game, convert_to_zero_based_player_number = false) + move_elements = self.downcase.split + player = move_elements[0].to_i + player = player - 1 if convert_to_zero_based_player_number + origin_index = move_elements[1].to_i + destination_index = move_elements[2].to_i + raise(InvalidMoveError, "Invalid origin #{origin_index} in move read") unless origin_index >= 0 and origin_index < game.board.positions.length + raise(InvalidMoveError, "Invalid destination #{destination_index} in move read") unless destination_index > 0 and destination_index <= game.board.positions.length + raise(InvalidMoveError, "Player #{player} does not have a piece on position #{origin_index} in move read") unless game.board.positions[origin_index].contains.any? {|p| p.player == player} + piece = game.board.positions[origin_index].contains.find {|p| p.player == player} + piece_name = piece.number + if move_elements.length == 4 + raise(InvalidMoveError, "Invalid card played: #{move_elements[3]}") unless $SYMBOLS.include?(move_elements[3].chomp.to_sym) + Move.new(game.pieces[player][piece_name], + game.board.positions[origin_index], + game.board.positions[destination_index], + move_elements[3].chomp.to_sym) + else + Move.new(game.pieces[player][piece_name], + game.board.positions[origin_index], + game.board.positions[destination_index]) + end + end +end + + +# Read a game description file and convert it into a Game object and set of Move objects. +# Note that Game.read_game method returns multiple values, so this one does too. +class IO + def IO.read_game(filename) + gamelines = IO.readlines(filename) + return Game.read_game(gamelines) + end +end + +# Extension to the Array class to include the Array#shuffle function +class Array + def shuffle + sort_by { rand } + end + + def index_find(&block) + found = find(&block) + if found + index found + else + found + end + end +end diff --git a/lib/.svn/text-base/libcartagena.rb.svn-base b/lib/.svn/text-base/libcartagena.rb.svn-base new file mode 100644 index 0000000..a0574d8 --- /dev/null +++ b/lib/.svn/text-base/libcartagena.rb.svn-base @@ -0,0 +1,722 @@ +# == Synopsis +# +# Library to support Cartagena play +# +# == Author +# Neil Smith +# +# 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 . +# +# == Change history +# Version 1.0:: 11 Jun 2008 +# * Initial build +# 1.1:: 5 Dec 2008 +# => * Now tracks played cards when generating new games + +# Symbols +$SYMBOLS = [:bottle, :dagger, :gun, :hat, :keys, :skull] + +# The number of cards of each symbol in the deck +$CARDS_PER_SYMBOL = 17 + +# The number of cards initiall dealt to each player +$INITIAL_CARDS_PER_PLAYER = 3 + +# Maximum number of pieces on a position +$MAX_PIECES_PER_POSITION = 3 + +# Number of actions that can be taken by each player in sequence +$MAX_MOVES_PER_TURN = 3 + +# Errors for a game + +# Moves can only be [1..6] spaces +class InvalidMoveError < StandardError +end + +# Game is won when only one player has uncaptured pieces +class GameWonNotice < StandardError +end + +# A position on the board. +class Position + attr_accessor :symbol + attr_accessor :contains + + def initialize(symbol) + @symbol = symbol + @contains = [] + end +end + +# A tile that makes up the board. Each tile has two sides, each with six +# positions. Tiles can be either way up, and either way round. +class Tile + attr_reader :exposed, :front, :back + + def initialize(front, back) + @front = front.collect {|s| Position.new(s)} + @back = back.collect {|s| Position.new(s)} + @exposed = @front + @exposed_side = :front + end + + def flip! + if @exposed_side == :front + @exposed = @back + @exposed_side = :back + else + @exposed = @front + @exposed_side = :front + end + end + + def reverse! + @exposed = @exposed.reverse + end +end + + +# The game board +class Board + + attr_accessor :positions + attr_reader :tiles + + # A laborious procedure to create all the positions and tie them all together + def initialize(tiles_used) + # A hash of all positions, indexed by position names + @tiles = [Tile.new([:hat, :keys, :gun, :bottle, :skull, :dagger], + [:hat, :keys, :gun, :bottle, :skull, :dagger]), + Tile.new([:gun, :hat, :dagger, :skull, :bottle, :keys], + [:gun, :hat, :dagger, :skull, :bottle, :keys]), + Tile.new([:skull, :gun, :bottle, :keys, :dagger, :hat], + [:skull, :gun, :bottle, :keys, :dagger, :hat]), + Tile.new([:dagger, :bottle, :keys, :gun, :hat, :skull], + [:dagger, :bottle, :keys, :gun, :hat, :skull]), + Tile.new([:keys, :dagger, :skull, :hat, :gun, :bottle], + [:keys, :dagger, :skull, :hat, :gun, :bottle]), + Tile.new([:bottle, :skull, :hat, :dagger, :keys, :gun], + [:bottle, :skull, :hat, :dagger, :keys, :gun]) + ].shuffle[0...tiles_used] + @tiles = @tiles.each do |t| + if rand < 0.5 + t.reverse! + else + t + end + end + + @positions = [Position.new(:cell)] + @tiles.each {|t| t.exposed.each {|p| @positions << p}} + @positions << Position.new(:boat) + end # def + + def to_s + layout + end + + def to_str + to_s + end + + + # For each position, show its name and what it touches + def layout + out_string = "" + @positions.each {|position| out_string << "#{position.symbol}\n"} + out_string + end + +end + + +# Each piece on the board is an object +class Piece + attr_reader :player, :number + attr_accessor :position + + def initialize(position, player, number) + @position = position + @player = player + @number = number + end + + def to_s + "#{@player}:#{@number}" + end + + def to_str + to_s + end + + def show(convert_to_zero_based_player_number = false) + if convert_to_zero_based_player_number + "#{@player + 1}:#{@number}" + else + "#{@player}:#{@number}" + end + end + + def move_to(new_position) + @position = new_position + end + +end + + +# A move in a game +class Move + attr_reader :piece, :origin, :destination, :card_played + + def initialize(piece, origin, destination, card_played = :unspecified) + @piece = piece + @origin = origin + @destination = destination + @card_played = card_played + end + + def show(board, convert_to_zero_based_player_number = false) + if @card_played == :unspecified + "#{@piece.show(convert_to_zero_based_player_number)}: #{board.positions.index(@origin)} -> #{board.positions.index(@destination)}" + else + "#{@piece.show(convert_to_zero_based_player_number)}: #{board.positions.index(@origin)} -> #{board.positions.index(@destination)} (#{@card_played})" + end + end + + def format(board, convert_to_zero_based_player_number = false) + display_player_number = if convert_to_zero_based_player_number then + @piece.player + 1 + else + @piece.player + end + if @card_played ==:unspecified + "#{display_player_number} #{board.positions.index(@origin)} #{board.positions.index(@destination)}" + else + "#{display_player_number} #{board.positions.index(@origin)} #{board.positions.index(@destination)} #{@card_played}" + end + end + + # Write a move to a string + # Note the inverse, String#to_move, is defined below + def to_s + "#{@piece.player} #{@origin.to_s} #{@destination.to_s} #{@card_played}" + end + + def to_str + to_s + end + +end + + + +# A class to record each of the states previously found in a game. +# Note that this is a deep copy of the pieces and what they've captured, so +# you'll need to convert back +class GameState + attr_accessor :move, :player, :board, :pieces_before_move, :deck_before_move, + :players_cards_before_move, :moves_by_current_player + # this_game_state = GameState.new(move, player, @board, @pieces, @deck, @players_cards) + + + def initialize(move, player, board, pieces, deck, players_cards, moves_by_current_player) + @move, @player, @board, @pieces_before_move, @deck_before_move, + @players_cards_before_move, @moves_by_current_player = + copy_game_state(move, player, board, pieces, deck, players_cards, + moves_by_current_player) + end + + # def ==(other) + # @move.to_s == other.move.to_s and + # @player == other.player and + # @piece_after_move == other.pieces_after_move + # end + + def copy_game_state(move, player, board, pieces, deck, players_cards, moves_by_current_player) + copy_player = player + moving_player = move.piece.player + + copy_board = board.dup + copy_board.positions = board.positions.collect {|pos| pos.dup} + copy_board.positions.each {|pos| pos.contains = []} + + copy_pieces = pieces.collect do |players_pieces| + players_pieces.collect do |piece| + new_piece_position = copy_board.positions[board.positions.index(piece.position)] + new_piece = Piece.new(new_piece_position, piece.player, piece.number) + new_piece_position.contains << new_piece + new_piece + end + end + + piece_index = pieces[moving_player].index(move.piece) + origin_index = board.positions.index(move.origin) + destination_index = board.positions.index(move.destination) + copy_move = Move.new(copy_pieces[moving_player][piece_index], + copy_board.positions[origin_index], + copy_board.positions[destination_index]) + + copy_deck = deck.dup + copy_players_cards = players_cards.collect {|p| p.dup} + copy_moves_by_current_player = moves_by_current_player + + return copy_move, copy_player, copy_board, copy_pieces, copy_deck, copy_players_cards, copy_moves_by_current_player + end + +end + + +# A game of Cartagena. It keeps a history of all previous states. +class Game + + attr_reader :history + attr_accessor :current_player + attr_reader :players + attr_accessor :players_cards + attr_accessor :moves_by_current_player + attr_reader :board + attr_reader :pieces + attr_reader :cards + attr_accessor :deck + + # Create a new game + def initialize(players = 5, number_of_tiles = 6, pieces_each = 6) + @board = Board.new(number_of_tiles) + @history = [] + @pieces = [] + @players = [].fill(0, players) {|i| i} + @players_cards = [] + @current_player = 0 + @moves_by_current_player = 0 + @cards = [] + 1.upto($CARDS_PER_SYMBOL) {|x| @cards.concat($SYMBOLS)} + @deck = @cards.shuffle + @players.each do |p| + @pieces[p] = [] + 0.upto(pieces_each - 1) do |count| + piece = Piece.new(@board.positions[0], p, count) + @pieces[p][count] = piece + @board.positions[0].contains << piece + end + @players_cards[p] = [] + deal_cards!($INITIAL_CARDS_PER_PLAYER, p) + end + end + + # Deal some cards to a player. Remove them from the deck. Refill the deck + # if necessary + def deal_cards!(number_of_cards, player = @current_player) + 1.upto(number_of_cards) do + if @deck.empty? + @deck = @cards + @players_cards.each do |p| + p.each do |c| + @deck.delete_at(@deck.index(c)) + end + end + @deck = @deck.shuffle + end + @players_cards[player] << @deck.pop + end + end + + # Check that a move is valid. Throw an exception if it's invalid + def validate_move(move, player = @current_player) + # Check the move is a valid one + raise(InvalidMoveError, "Move #{move}: Player #{player} does not exist") unless @players.include?(player) + raise(InvalidMoveError, "Move #{move}: None of player #{player}'s pieces on position #{move.origin}") unless move.origin.contains.find {|pc| pc.player == player} + raise(InvalidMoveError, "Move #{move}: Origin and destination are the same") if move.origin == move.destination + + origin_position = @board.positions.index(move.origin) + destination_position = @board.positions.index(move.destination) + # Is this move an advance or a retreat? + if destination_position > origin_position + # Advancing a piece + if move.destination == @board.positions[-1] # A move into the boat + raise(InvalidMoveError, "Move #{move}: Move into boat and card unspecified") if move.destination == @board.positions[-1] and move.card_played == :unspecified + unless @players_cards[player].find {|c| c == move.card_played} + raise(InvalidMoveError, "Player #{player} does not have a card to move a piece into the boat") + end + else + if move.card_played != :unspecified and move.destination.symbol != move.card_played + raise(InvalidMoveError, "Player #{player} trying to move to #{move.destination}, a #{move.destination.symbol} square with a a #{move.card_played} card") + end + unless @players_cards[player].find {|c| c == move.destination.symbol} + raise(InvalidMoveError, "Player #{player} does not have a card to move a piece to a #{move.destination.symbol} square") + end + # Check target square is vacant + raise(InvalidMoveError, "Advance move #{move}: destination occupied") unless move.destination.contains.empty? + end + # Check all the intervening squares with this symbol are occupied + intervening_empty_position = @board.positions[origin_position...destination_position].index_find do |p| + p.symbol == move.destination.symbol and + p.contains.empty? + end + raise(InvalidMoveError, "Advance move #{move}: location #{intervening_empty_position} is empty") if intervening_empty_position + else + # Retreating a piece + # Check target position has one or two pieces already on it + destination_count = move.destination.contains.length + raise(InvalidMoveError, "Retreat move #{move}: destination has no pieces already on it") if destination_count == 0 + raise(InvalidMoveError, "Retreat move #{move}: destination has too many (#{destination_count}) pieces already on it") if destination_count >= $MAX_PIECES_PER_POSITION + # Check none of the intervening squares have any pieces on them + # puts "Checking positions #{destination_position} to #{origin_position}" + intervening_target_position = @board.positions[(destination_position + 1)...origin_position].index_find do |p| + # puts "Examining postition #{p} at location #{@board.positions.index(p)} which contains #{p.contains.length} pieces" + p.contains.length > 0 and + p.contains.length < $MAX_PIECES_PER_POSITION + end + raise(InvalidMoveError, "Retreat move #{move.show(@board)}: location #{intervening_target_position} is a viable target") if intervening_target_position + end + end + + # Apply a single move to a game. + def apply_move!(move, player = @current_player, validate = true) + + validate_move(move, player) if validate + + raise(InvalidMoveError, "Too many consecutive moves by #{@current_player}: has taken #{@moves_by_current_player} and validate is #{validate}") if validate and player == @current_player and @moves_by_current_player >= $MAX_MOVES_PER_TURN + + # Record the old state + this_game_state = GameState.new(move, @current_player, @board, @pieces, @deck, @players_cards, @moves_by_current_player) + @history << this_game_state + + # Apply this move + move.origin.contains.delete move.piece + move.destination.contains << move.piece + move.piece.position = move.destination + + if player == @current_player + @moves_by_current_player += 1 + else + @current_player = player + @moves_by_current_player = 1 + end + + # Update cards + if @board.positions.index(move.destination) > @board.positions.index(move.origin) + # Advance move + if validate + card_to_remove = if move.card_played != :unspecified + move.card_played + else + move.destination.symbol + end + if @players_cards[player].include?(card_to_remove) + @players_cards[player].delete_at(@players_cards[player].index(card_to_remove)) + end + end + else + # Retreat move + deal_cards!(move.destination.contains.length - 1, player) + end + + # If this player has all their pieces in the boat, declare a win. + if @pieces[player].all? {|pc| pc.position == @board.positions[-1]} + raise(GameWonNotice, "Game won by #{player}") + end + end + + # Undo a move + def undo_move! + state_to_restore = @history[-1] + move, @current_player, @board, @pieces, @deck, @players_cards, + @moves_by_current_player = + state_to_restore.copy_game_state(state_to_restore.move, + state_to_restore.player, + state_to_restore.board, + state_to_restore.pieces_before_move, + state_to_restore.deck_before_move, + state_to_restore.players_cards_before_move, + state_to_restore.moves_by_current_player) + @history.pop + end + + # Apply a list of moves in order + def apply_moves!(moves) + moves.each do |move| + @current_player = move.piece.player + apply_move!(move, @current_player) + next_player! if @moves_by_current_player >= $MOVES_PER_TURN + end + end + + + # Set the current player to be the next player + def next_player! + if @current_player == @players[-1] + @current_player = @players[0] + else + @current_player = @players[@players.index(@current_player) + 1] + end + @moves_by_current_player = 0 + @current_player + end + + + def reset_current_player(new_current_player) + @current_player = new_current_player + @moves_by_current_player = 0 + end + + + # Return an array of all possible moves from this state, given the active player + def possible_moves(player = @current_player) + moves = [] + @pieces[player].each do |piece| + # Do a forward move for each card held + unless piece.position == @board.positions[-1] + @players_cards[player].each do |card| + destination = @board.positions[@board.positions.index(piece.position)..-1].find do |pos| + (pos.symbol == card and pos.contains == []) or + pos.symbol == :boat + end + # puts "Player #{player}, card #{card}, piece #{piece.number} at position #{@board.positions.index(piece.position)} to #{@board.positions.index(destination)}, a #{destination.symbol}" + moves << Move.new(piece, piece.position, destination, card) + end + end + # Do a reverse move for the piece + unless piece.position == board.positions[0] + destination = @board.positions[1...@board.positions.index(piece.position)].reverse.find do |pos| + pos.contains.length == 1 or pos.contains.length == 2 + end + if destination + # puts "Player #{player}, piece #{piece.number} at position #{@board.positions.index(piece.position)} retreats to #{@board.positions.index(destination)}, a #{destination.symbol} containing #{destination.contains.length} pieces" + moves << Move.new(piece, piece.position, destination) + end + end + end + # moves.each {|m| puts m.show(@board)} + moves + end + + def build_state_string + outstr = "Current player = #{@current_player}\n" + 0.upto((@board.positions.length)-1) do |i| + outstr << "#{i}: #{@board.positions[i].symbol}: " + @board.positions[i].contains.each do |piece| + outstr << "P#{piece.player}:#{piece.number} " + end + outstr << "\n" + end + 0.upto((@players.length)-1) do |i| + outstr << "Player #{i} holds " << (@players_cards[i].sort_by {|c| c.to_s}).join(', ') << "\n" + end + outstr << "Deck holds " << @deck.join(', ') << "\n" + outstr + end + + # Show the state of the board + def show_state + puts build_state_string + end + + def to_s + show_state + end + + def to_str + to_s + end + + + def set_testing_game! + srand 1234 + @board = nil + initialize(5, 6, 6) + board_symbols = [:cell, :gun, :keys, :dagger, :hat, :skull, :bottle, + :keys, :dagger, :skull, :hat, :gun, :bottle, + :dagger, :bottle, :keys, :gun, :hat, :skull, + :dagger, :skull, :bottle, :gun, :keys, :hat, + :hat, :dagger, :keys, :bottle, :gun, :skull, + :keys, :bottle, :skull, :dagger, :hat, :gun, :boat] + board_symbols.each_index do |i| + @board.positions[i].symbol = board_symbols[i] + end + + @players_cards[0] = [:gun, :hat, :skull] + @players_cards[1] = [:bottle, :keys, :keys] + @players_cards[2] = [:bottle, :hat, :keys] + @players_cards[3] = [:bottle, :skull, :skull] + @players_cards[4] = [:bottle, :gun, :gun] + + @deck = [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle, :skull] + + end + + # Given a set of lines from an input file, turn them into a Game object and + # a set of Move objects. + # Note the multiple return values. + # Class method + def Game.read_game(gamelines) + gamelines.each {|l| l.chomp!} + game = Game.new(gamelines[0].to_i, 6, 6) + + # create the board + 2.upto(38) do |i| + game.board.positions[i-1] = Position.new(gamelines[i].to_sym) + end + + # read all the moves so far + moves = [] + i = 39 + current_player_moves = 0 + current_player = 0 + player_card_counts = game.players_cards.map {|p| $INITIAL_CARDS_PER_PLAYER} + unused_cards = game.cards.map {|c| c} + while gamelines[i].in_move_format? + move = gamelines[i].to_move(game, true) + if move.piece.player == current_player + current_player_moves += 1 + else + current_player = move.piece.player + current_player_moves = 1 + end + # if move is an advance move, decrement the cards held by the player and remove the card from unused cards + # throw an error if an advance move is made when all the cards of that type have been used + # if unused_cards becomes empty, restock it. + if game.board.positions.index(move.destination) > game.board.positions.index(move.origin) + raise(InvalidMoveError, "Game reading: Player #{move.piece.player} attempting an advance move with no cards in hand. Move is #{gamelines[i]} on line #{i}") if player_card_counts[move.piece.player] == 0 + card_used = move.destination.symbol + if card_used == :boat + card_used = move.card_played + end + raise(InvalidMoveError, "Attempting to move into boat without a card specified") if card_used == :unspecified + raise(InvalidMoveError, "Game reading: Player #{move.piece.player} attempting an advance move to a #{move.destination.symbol} place when all those cards have been used. Move is #{gamelines[i]} on line #{i}") unless unused_cards.include? card_used + player_card_counts[current_player] -= 1 + unused_cards.delete_at(unused_cards.index(card_used)) + # if move is a retreat move, increment the cards held by the player + else + player_card_counts[current_player] += move.destination.contains.length + if unused_cards.length == player_card_counts.inject {|sum, n| sum + n } + unused_cards = game.cards.map {|c| c} + end + end + game.apply_move!(move, move.piece.player, false) + moves << move + i = i + 1 + end + + # note the current player + game.current_player = gamelines[i].to_i - 1 + if game.current_player == current_player + game.moves_by_current_player = current_player_moves + else + game.moves_by_current_player = 0 + end + current_player = game.current_player + + # read the cards + game.players_cards = game.players_cards.map {|c| []} + (i+1).upto(gamelines.length - 1) do |j| + game.players_cards[game.current_player] << gamelines[j].to_sym + raise(InvalidMoveError, "Player #{game.current_player} given a #{gamelines[j]} card, but none left in deck") unless unused_cards.index(gamelines[j].to_sym) + unused_cards.delete_at(unused_cards.index(gamelines[j].to_sym)) + end + raise(InvalidMoveError, "Player #{game.current_player} given #{game.players_cards[game.current_player].length} cards, but should have #{player_card_counts[game.current_player]} cards from play") if game.players_cards[game.current_player].length != player_card_counts[game.current_player] + + # Update the game deck + game.deck = unused_cards.shuffle + player_card_counts[game.current_player] = 0 + player_card_counts.each_index do |player| + if player_card_counts[player] > 0 + game.deal_cards!(player_card_counts[player], player) + end + end + return game, moves + end + +end + + +# Extension to String class to convert a move-description string into a Move object. +# This is the inverse of the Move#to_s method +class String + def in_move_format? + elements = split + return ((elements.length == 3 or elements.length == 4) and + elements[0].to_i > 0 and elements[0].to_i < 6 and + elements[1].to_i >= 0 and elements[1].to_i <= 37 and + elements[2].to_i >= 0 and elements[2].to_i <= 37) + end + + def to_move(game, convert_to_zero_based_player_number = false) + move_elements = self.downcase.split + player = move_elements[0].to_i + player = player - 1 if convert_to_zero_based_player_number + origin_index = move_elements[1].to_i + destination_index = move_elements[2].to_i + raise(InvalidMoveError, "Invalid origin #{origin_index} in move read") unless origin_index >= 0 and origin_index < game.board.positions.length + raise(InvalidMoveError, "Invalid destination #{destination_index} in move read") unless destination_index > 0 and destination_index <= game.board.positions.length + raise(InvalidMoveError, "Player #{player} does not have a piece on position #{origin_index} in move read") unless game.board.positions[origin_index].contains.any? {|p| p.player == player} + piece = game.board.positions[origin_index].contains.find {|p| p.player == player} + piece_name = piece.number + if destination_index > origin_index + if move_elements.length == 4 + card_used = move_elements[3].chomp.to_sym + raise(InvalidMoveError, "Card used (#{card_used}) does not match destination space (#{game.board.positions[destination_index].symbol})") unless card_used == game.board.positions[destination_index].symbol or destination_index == game.board.positions.length - 1 + Move.new(game.pieces[player][piece_name], + game.board.positions[origin_index], + game.board.positions[destination_index], + card_used) + else + card_used = :unspecified + raise(InvalidMoveError, "Move to boat without specifying card used") if destination_index == game.board.positions.length + 1 + Move.new(game.pieces[player][piece_name], + game.board.positions[origin_index], + game.board.positions[destination_index]) + end + else + Move.new(game.pieces[player][piece_name], + game.board.positions[origin_index], + game.board.positions[destination_index]) + end + end +end + + +# Read a game description file and convert it into a Game object and set of Move objects. +# Note that Game.read_game method returns multiple values, so this one does too. +class IO + def IO.read_game(filename) + gamelines = IO.readlines(filename) + return Game.read_game(gamelines) + end +end + +# Extension to the Array class to include the Array#shuffle function +class Array + def shuffle + sort_by { rand } + end + + def index_find(&block) + found = find(&block) + if found + index found + else + found + end + end +end diff --git a/lib/.svn/text-base/main.rb.svn-base b/lib/.svn/text-base/main.rb.svn-base new file mode 100644 index 0000000..bf9b84d --- /dev/null +++ b/lib/.svn/text-base/main.rb.svn-base @@ -0,0 +1,6 @@ +# +# To change this template, choose Tools | Templates +# and open the template in the editor. + + +puts "Hello World" diff --git a/lib/.svn/text-base/random-move.rb.netbeans-base b/lib/.svn/text-base/random-move.rb.netbeans-base new file mode 100644 index 0000000..53f6c5f --- /dev/null +++ b/lib/.svn/text-base/random-move.rb.netbeans-base @@ -0,0 +1,27 @@ +#!/usr/bin/ruby +# +# == Synopsis +# +# Play one move of a Cartagena game +# +# == Usage +# clockwise-p1 +# Game state file read on STDIN +# Move produced on STDOUT +# +# == Author +# Neil Smith + +# +# To change this template, choose Tools | Templates +# and open the template in the editor. + + +require 'libcartagena' +require 'rdoc/usage' + +gamelines = readlines +game, moves = Game.read_game(gamelines) +possibles = game.possible_moves +move = possibles[rand(possibles.length)] +puts move.format(game.board, true) diff --git a/lib/.svn/text-base/random-move.rb.svn-base b/lib/.svn/text-base/random-move.rb.svn-base new file mode 100644 index 0000000..53f6c5f --- /dev/null +++ b/lib/.svn/text-base/random-move.rb.svn-base @@ -0,0 +1,27 @@ +#!/usr/bin/ruby +# +# == Synopsis +# +# Play one move of a Cartagena game +# +# == Usage +# clockwise-p1 +# Game state file read on STDIN +# Move produced on STDOUT +# +# == Author +# Neil Smith + +# +# To change this template, choose Tools | Templates +# and open the template in the editor. + + +require 'libcartagena' +require 'rdoc/usage' + +gamelines = readlines +game, moves = Game.read_game(gamelines) +possibles = game.possible_moves +move = possibles[rand(possibles.length)] +puts move.format(game.board, true) diff --git a/lib/.svn/tmp/tempfile.2.tmp b/lib/.svn/tmp/tempfile.2.tmp new file mode 100644 index 0000000..220ac25 --- /dev/null +++ b/lib/.svn/tmp/tempfile.2.tmp @@ -0,0 +1,497 @@ +# == Synopsis +# +# Library to support Cartagena play +# +# == Author +# Neil Smith +# +# 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 . +# +# == Change history +# Version 1.0:: 11 Jun 2008 +# * Initial build + +# Symbols +$SYMBOLS = [:bottle, :dagger, :gun, :hat, :keys, :skull] + +# The number of cards of each symbol in the deck +$CARDS_PER_SYMBOL = 17 + +# The number of cards initiall dealt to each player +$INITIAL_CARDS_PER_PLAYER = 3 + +# Maximum number of pieces on a position +$MAX_PIECES_PER_POSITION = 3 + +# Errors for a game + +# Moves can only be [1..6] spaces +class InvalidMoveError < StandardError +end + +# Game is won when only one player has uncaptured pieces +class GameWonNotice < StandardError +end + +# A position on the board. +class Position + attr_reader :symbol, :contains + + def initialize(symbol) + @symbol = symbol + @contains = [] + end +end + +# A tile that makes up the board. Each tile has two sides, each with six +# positions. Tiles can be either way up, and either way round. +class Tile + attr_reader :exposed, :front, :back + + def initialize(front, back) + @front = front.collect {|s| Position.new(s)} + @back = back.collect {|s| Position.new(s)} + @exposed = @front + @exposed_side = :front + end + + def flip! + if @exposed_side == :front + @exposed = @back + @exposed_side = :back + else + @exposed = @front + @exposed_side = :front + end + end + + def reverse! + @exposed = @exposed.reverse + end +end + + +# The game board +class Board + + attr_reader :positions + attr_reader :tiles + + # A laborious procedure to create all the positions and tie them all together + def initialize(tiles_used) + # A hash of all positions, indexed by position names + @tiles = [Tile.new([:hat, :keys, :gun, :bottle, :skull, :dagger], + [:hat, :keys, :gun, :bottle, :skull, :dagger]), + Tile.new([:gun, :hat, :dagger, :skull, :bottle, :keys], + [:gun, :hat, :dagger, :skull, :bottle, :keys]), + Tile.new([:skull, :gun, :bottle, :keys, :dagger, :hat], + [:skull, :gun, :bottle, :keys, :dagger, :hat]), + Tile.new([:dagger, :bottle, :keys, :gun, :hat, :skull], + [:dagger, :bottle, :keys, :gun, :hat, :skull]), + Tile.new([:keys, :dagger, :skull, :hat, :gun, :bottle], + [:keys, :dagger, :skull, :hat, :gun, :bottle]), + Tile.new([:bottle, :skull, :hat, :dagger, :keys, :gun], + [:bottle, :skull, :hat, :dagger, :keys, :gun]) + ].shuffle[0...tiles_used] + @tiles = @tiles.each do |t| + if rand < 0.5 + t.reverse! + else + t + end + end + + @positions = [Position.new(:cell)] + @tiles.each {|t| t.exposed.each {|p| @positions << p}} + @positions << Position.new(:boat) + end # def + + def to_s + layout + end + + def to_str + to_s + end + + + # For each position, show its name and what it touches + def layout + out_string = "" + @positions.each {|position| out_string << "#{position}\n"} + out_string + end + +end + + +# Each piece on the board is an object +class Piece + attr_reader :player, :number + attr_accessor :position + + def initialize(position, player, number) + @position = position + @player = player + @number = number + end + + def to_s + "#{@player}:#{@number}" + end + + def to_str + to_s + end + + def move_to(new_position) + @position = new_position + end + +end + + +# A move in a game +class Move + attr_reader :piece, :origin, :destination + + def initialize(piece, origin, destination) + @piece = piece + @origin = origin + @destination = destination + end + + def show(board) + "#{@piece.to_s}: #{board.positions.index(@origin)} -> #{board.positions.index(@destination)}" + end + + # Write a move to a string + # Note the inverse, String#to_move, is defined below + def to_s + "#{@piece.player}: #{@origin.to_s} -> #{@destination.to_s}" + end + + def to_str + to_s + end + +end + + + +# A class to record each of the states previously found in a game. +# Note that this is a deep copy of the pieces and what they've captured, so +# you'll need to convert back +class GameState + attr_accessor :move, :player, :pieces_after_move + + def initialize(move, player, pieces) + @move = move + @player = player + @pieces_after_move = Hash.new + pieces.each {|k, p| @pieces_after_move[k] = p.dup} + end + + def ==(other) + @move.to_s == other.move.to_s and + @player == other.player and + @piece_after_move == other.pieces_after_move + end + +end + + +# A game of Cartagena. It keeps a history of all previous states. +class Game + + attr_reader :history + attr_reader :current_player, :players, :players_cards + attr_reader :board + attr_reader :pieces + attr_reader :deck + + # Create a new game + def initialize(players = 6, number_of_tiles = 6, pieces_each = 6) + @board = Board.new(number_of_tiles) + @history = [] + @pieces = [] + @players = [].fill(0, players) {|i| i} + @players_cards = [] + @current_player = 0 + @cards = [] + 1.upto($CARDS_PER_SYMBOL) {|x| @cards.concat($SYMBOLS)} + @deck = @cards.shuffle + @players.each do |p| + @pieces[p] = [] + 0.upto(pieces_each - 1) do |count| + piece = Piece.new(@board.positions[0], p, count) + @pieces[p][count] = piece + @board.positions[0].contains << piece + end + @players_cards[p] = [] + deal_cards!($INITIAL_CARDS_PER_PLAYER, p) + end + end + + def deal_cards!(number_of_cards, player = @current_player) + 1.upto(number_of_cards) do + @deck = @cards.shuffle if @deck.empty? + @players_cards[player] << @deck.pop + end + end + + # Check that a move is valid. Throw an exception if it's invalid + def validate_move(move, player = @current_player) + # Check the move is a valid one + raise(InvalidMoveError, "Move #{move}: Player #{player} does not exist") unless @players.include?(player) + raise(InvalidMoveError, "Move #{move}: None of player #{player}'s pieces on position #{move.origin}") unless move.origin.contains.find {|pc| pc.player == player} + raise(InvalidMoveError, "Move #{move}: Origin and destination are the same") if move.origin == move.destination + + origin_position = @board.positions.index(move.origin) + destination_position = @board.positions.index(move.destination) + # Is this move an advance or a retreat? + if destination_position > origin_position + # Advancing a piece + unless @players_cards[player].find {|c| c == move.destination.symbol} + raise(InvalidMoveError, "Player #{player} does not have a card to move a piece to a #{move.destination.symbol} square") + end + # Check target square is vacant + raise(InvalidMoveError, "Advance move #{move}: destination occupied") unless move.destination.contains.empty? + # Check all the intervening squares with this symbol are occupied + intervening_empty_position = @board.positions[origin_position...destination_position].index_find do |p| + p.symbol == move.destination.symbol and + p.contains.empty? + end + raise(InvalidMoveError, "Advance move #{move}: location #{intervening_empty_position} is empty") if intervening_empty_position + else + # Retreating a piece + # Check target position has one or two pieces already on it + destination_count = move.destination.contains.length + raise(InvalidMoveError, "Retreat move #{move}: destination has no pieces already on it") if destination_count == 0 + raise(InvalidMoveError, "Retreat move #{move}: destination has too many (#{destination_count}) pieces already on it") if destination_count >= $MAX_PIECES_PER_POSITION + # Check none of the intervening squares have any pieces on them + intervening_target_position = @board.positions[destination_position...origin_position].index_find do |p| + p.contains.length > 0 and + p.contains.length < $MAX_PIECES_PER_POSITION + end + raise(InvalidMoveError, "Retreat move #{move}: location #{intervening_target_position} is a viable target") if intervening_target_position + end + end + + # Apply a single move to a game. + def apply_move!(move, player = @current_player) + + validate_move(move, player) + + # Apply this move + move.origin.contains.delete move.piece + move.destination.contains << move.piece + move.piece.position = move.destination + + # Update cards + if @board.positions.index(move.destination) > @board.positions.index(move.origin) + # Advance move + @players_cards[player].delete_at(@players_cards[player].index(move.destination.symbol)) + else + # Retreat move + deal_cards!(move.destination.contains.length, player) + end + + # Record the new stae +# this_game_state = GameState.new(move, player, @pieces) +# @history << this_game_state + + # If this player has all their pieces in the boat, declare a win. + if @pieces[player].all? {|pc| pc.position == :boat} + raise(GameWonNotice, "Game won by #{player}") + end + end + + # Undo a move + def undo_move! + if @history.length > 1 + # general case + state_to_restore = @history[-2] + @current_player = @history[-1].player + @pieces.each do |name, piece| + copy_piece = state_to_restore.pieces_after_move[name] + piece.position = copy_piece.position + end + @history.pop + elsif @history.length == 1 + # reset to start + @current_player = @players[1] + @pieces.each do |name, piece| + piece.position = @board.positions[piece.colour] + end + @history.pop + end + end + + # Apply a list of moves in order + def apply_moves!(moves) + moves.each do |move| + if move.via_base? + moved_distance = board.distance_between[move.piece.position.place][@current_player] + + board.distance_between[@current_player][move.destination.place] + else + moved_distance = board.distance_between[move.piece.position.place][move.destination.place] + end + self.apply_move!(move, @current_player) + next_player! unless moved_distance == 6 + end + end + + + # Set the current player to be the next player + def next_player! + original_player = @current_player + if @current_player == @players[-1] + @current_player = @players[1] + else + @current_player = @players[@players.index(@current_player) + 1] + end + @current_player + end + + + # Return an array of all possible moves from this state, given the active player + def possible_moves(player = @current_player) + moves = [] + @pieces[player].each do |piece| + # Do a forward move for each card held + unless piece.position == @board.positions[-1] + @players_cards[player].each do |card| + puts "Player #{player}, card #{card}, piece #{piece.number} at position #{@board.positions.index(piece.position)}" + destination = @board.positions[@board.positions.index(piece.position)..-1].find do |pos| + (pos.symbol == card and pos.contains == []) or + pos.symbol == :boat + end + moves << Move.new(piece, piece.position, destination) + end + end + # Do a reverse move for the piece + unless piece.position == board.positions[0] + destination = @board.positions[0...@board.positions.index(piece.position)].reverse.find do |pos| + pos.contains.length == 1 or pos.contains.length == 2 + end + moves << Move.new(piece, piece.position, destination) + end + end + moves + end + + + def build_state_string + outstr = "Current player = #{@current_player}\n" + 0.upto((@board.positions.length)-1) do |i| + outstr << "#{i}: #{@board.positions[i].symbol}: " + @board.positions[i].contains.each do |piece| + outstr << "P#{piece.player}:#{piece.number} " + end + outstr << "\n" + end + 0.upto((@players.length)-1) do |i| + outstr << "Player #{i} holds " << (@players_cards[i].sort_by {|c| c.to_s}).join(', ') << "\n" + end + outstr << "Deck holds " << @deck.join(', ') << "\n" + outstr + end + + # Show the state of the board + def show_state + puts build_state_string + # @pieces.keys.sort.each do |piece_name| + # if @pieces[piece_name].captured + # puts "Piece #{piece_name} captured, at #{@pieces[piece_name].position}" + # else + # puts "Piece #{piece_name} is at #{@pieces[piece_name].position}, holds #{(@pieces[piece_name].contains.collect{|c| c.name}).join(' ')}" + # end + # end + end + + def to_s + show_state + end + + def to_str + to_s + end + + + # Given a set of lines from an input file, turn them into a Game object and + # a set of Move objects. + # Note the multiple return values. + # Class method + def Game.read_game(gamelines) + gamelines.each {|l| l.chomp!} + game = Game.new(gamelines[0].to_i, 6, 6, 6, 6) + moves = [] + gamelines[1..-2].each {|m| moves << m.to_move(game)} + return game, moves, gamelines[-1].to_i + end + + +end + + +# Extension to String class to convert a move-description string into a Move object. +# This is the inverse of the Move#to_s method +class String + def to_move(game) + move_elements = self.downcase.split + piece_name = move_elements[0] + destination_name = move_elements[-1] + if destination_name.length > 2 and + destination_name[-2,2] == game.board.centre.place[-2,2] + destination_name = game.board.centre.place + end + raise(InvalidMoveError, "Invalid piece in move read") unless game.pieces.has_key?(piece_name) + raise(InvalidMoveError, "Invalid destination in move read") unless game.board.positions.has_key?(destination_name) + # Deal with the synonyms for the centre position + via_base = (destination_name.length == 1 or move_elements.length > 2) + Move.new(game.pieces[piece_name], game.board.positions[destination_name], via_base) + end +end + + +# Read a game description file and convert it into a Game object and set of Move objects. +# Note that Game.read_game method returns multiple values, so this one does too. +class IO + def IO.read_game(filename) + gamelines = IO.readlines(filename) + return Game.read_game(gamelines) + end +end + +# Extension to the Array class to include the Array#shuffle function +class Array + def shuffle + sort_by { rand } + end + +<<<<<<< .mine + def index_find(&proc) + self.index(self.find &proc) # added some additional comments +======= + def index_find(&block) + found = find(&block) + if found + index found + else + found + end +>>>>>>> .r32 + end +end diff --git a/lib/.svn/tmp/tempfile.3.tmp b/lib/.svn/tmp/tempfile.3.tmp new file mode 100644 index 0000000..8c1eb00 --- /dev/null +++ b/lib/.svn/tmp/tempfile.3.tmp @@ -0,0 +1,503 @@ +# == Synopsis +# +# Library to support Cartagena play +# +# == Author +# Neil Smith +# +# 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 . +# +# == Change history +# Version 1.0:: 11 Jun 2008 +# * Initial build + +# Symbols +$SYMBOLS = [:bottle, :dagger, :gun, :hat, :keys, :skull] + +# The number of cards of each symbol in the deck +$CARDS_PER_SYMBOL = 17 + +# The number of cards initiall dealt to each player +$INITIAL_CARDS_PER_PLAYER = 3 + +# Maximum number of pieces on a position +$MAX_PIECES_PER_POSITION = 3 + +# Errors for a game + +# Moves can only be [1..6] spaces +class InvalidMoveError < StandardError +end + +# Game is won when only one player has uncaptured pieces +class GameWonNotice < StandardError +end + +# A position on the board. +class Position + attr_reader :symbol, :contains + + def initialize(symbol) + @symbol = symbol + @contains = [] + end +end + +# A tile that makes up the board. Each tile has two sides, each with six +# positions. Tiles can be either way up, and either way round. +class Tile + attr_reader :exposed, :front, :back + + def initialize(front, back) + @front = front.collect {|s| Position.new(s)} + @back = back.collect {|s| Position.new(s)} + @exposed = @front + @exposed_side = :front + end + + def flip! + if @exposed_side == :front + @exposed = @back + @exposed_side = :back + else + @exposed = @front + @exposed_side = :front + end + end + + def reverse! + @exposed = @exposed.reverse + end +end + + +# The game board +class Board + + attr_reader :positions + attr_reader :tiles + + # A laborious procedure to create all the positions and tie them all together + def initialize(tiles_used) + # A hash of all positions, indexed by position names + @tiles = [Tile.new([:hat, :keys, :gun, :bottle, :skull, :dagger], + [:hat, :keys, :gun, :bottle, :skull, :dagger]), + Tile.new([:gun, :hat, :dagger, :skull, :bottle, :keys], + [:gun, :hat, :dagger, :skull, :bottle, :keys]), + Tile.new([:skull, :gun, :bottle, :keys, :dagger, :hat], + [:skull, :gun, :bottle, :keys, :dagger, :hat]), + Tile.new([:dagger, :bottle, :keys, :gun, :hat, :skull], + [:dagger, :bottle, :keys, :gun, :hat, :skull]), + Tile.new([:keys, :dagger, :skull, :hat, :gun, :bottle], + [:keys, :dagger, :skull, :hat, :gun, :bottle]), + Tile.new([:bottle, :skull, :hat, :dagger, :keys, :gun], + [:bottle, :skull, :hat, :dagger, :keys, :gun]) + ].shuffle[0...tiles_used] + @tiles = @tiles.each do |t| + if rand < 0.5 + t.reverse! + else + t + end + end + + @positions = [Position.new(:cell)] + @tiles.each {|t| t.exposed.each {|p| @positions << p}} + @positions << Position.new(:boat) + end # def + + def to_s + layout + end + + def to_str + to_s + end + + + # For each position, show its name and what it touches + def layout + out_string = "" + @positions.each {|position| out_string << "#{position.symbol}\n"} + out_string + end + +end + + +# Each piece on the board is an object +class Piece + attr_reader :player, :number + attr_accessor :position + + def initialize(position, player, number) + @position = position + @player = player + @number = number + end + + def to_s + "#{@player}:#{@number}" + end + + def to_str + to_s + end + + def move_to(new_position) + @position = new_position + end + +end + + +# A move in a game +class Move + attr_reader :piece, :origin, :destination + + def initialize(piece, origin, destination) + @piece = piece + @origin = origin + @destination = destination + end + + def show(board) + "#{@piece.to_s}: #{board.positions.index(@origin)} -> #{board.positions.index(@destination)}" + end + + # Write a move to a string + # Note the inverse, String#to_move, is defined below + def to_s + "#{@piece.player}: #{@origin.to_s} -> #{@destination.to_s}" + end + + def to_str + to_s + end + +end + + + +# A class to record each of the states previously found in a game. +# Note that this is a deep copy of the pieces and what they've captured, so +# you'll need to convert back +class GameState + attr_accessor :move, :player, :pieces_after_move + + def initialize(move, player, pieces) + @move = move + @player = player + @pieces_after_move = Hash.new + pieces.each {|k, p| @pieces_after_move[k] = p.dup} + end + + def ==(other) + @move.to_s == other.move.to_s and + @player == other.player and + @piece_after_move == other.pieces_after_move + end + +end + + +# A game of Cartagena. It keeps a history of all previous states. +class Game + + attr_reader :history + attr_reader :current_player, :players, :players_cards + attr_reader :board + attr_reader :pieces + attr_reader :deck + + # Create a new game + def initialize(players = 6, number_of_tiles = 6, pieces_each = 6) + @board = Board.new(number_of_tiles) + @history = [] + @pieces = [] + @players = [].fill(0, players) {|i| i} + @players_cards = [] + @current_player = 0 + @cards = [] + 1.upto($CARDS_PER_SYMBOL) {|x| @cards.concat($SYMBOLS)} + @deck = @cards.shuffle + @players.each do |p| + @pieces[p] = [] + 0.upto(pieces_each - 1) do |count| + piece = Piece.new(@board.positions[0], p, count) + @pieces[p][count] = piece + @board.positions[0].contains << piece + end + @players_cards[p] = [] + deal_cards!($INITIAL_CARDS_PER_PLAYER, p) + end + end + + def deal_cards!(number_of_cards, player = @current_player) + 1.upto(number_of_cards) do + @deck = @cards.shuffle if @deck.empty? + @players_cards[player] << @deck.pop + end + end + + # Check that a move is valid. Throw an exception if it's invalid + def validate_move(move, player = @current_player) + # Check the move is a valid one + raise(InvalidMoveError, "Move #{move}: Player #{player} does not exist") unless @players.include?(player) + raise(InvalidMoveError, "Move #{move}: None of player #{player}'s pieces on position #{move.origin}") unless move.origin.contains.find {|pc| pc.player == player} + raise(InvalidMoveError, "Move #{move}: Origin and destination are the same") if move.origin == move.destination + + origin_position = @board.positions.index(move.origin) + destination_position = @board.positions.index(move.destination) + # Is this move an advance or a retreat? + if destination_position > origin_position + # Advancing a piece + unless @players_cards[player].find {|c| c == move.destination.symbol} + raise(InvalidMoveError, "Player #{player} does not have a card to move a piece to a #{move.destination.symbol} square") + end + # Check target square is vacant + raise(InvalidMoveError, "Advance move #{move}: destination occupied") unless move.destination.contains.empty? + # Check all the intervening squares with this symbol are occupied + intervening_empty_position = @board.positions[origin_position...destination_position].index_find do |p| + p.symbol == move.destination.symbol and + p.contains.empty? + end + raise(InvalidMoveError, "Advance move #{move}: location #{intervening_empty_position} is empty") if intervening_empty_position + else + # Retreating a piece + # Check target position has one or two pieces already on it + destination_count = move.destination.contains.length + raise(InvalidMoveError, "Retreat move #{move}: destination has no pieces already on it") if destination_count == 0 + raise(InvalidMoveError, "Retreat move #{move}: destination has too many (#{destination_count}) pieces already on it") if destination_count >= $MAX_PIECES_PER_POSITION + # Check none of the intervening squares have any pieces on them +# puts "Checking positions #{destination_position} to #{origin_position}" + intervening_target_position = @board.positions[(destination_position + 1)...origin_position].index_find do |p| +# puts "Examining postition #{p} at location #{@board.positions.index(p)} which contains #{p.contains.length} pieces" + p.contains.length > 0 and + p.contains.length < $MAX_PIECES_PER_POSITION + end + raise(InvalidMoveError, "Retreat move #{move.show(@board)}: location #{intervening_target_position} is a viable target") if intervening_target_position + end + end + + # Apply a single move to a game. + def apply_move!(move, player = @current_player) + + validate_move(move, player) + + # Apply this move + move.origin.contains.delete move.piece + move.destination.contains << move.piece + move.piece.position = move.destination + + # Update cards + if @board.positions.index(move.destination) > @board.positions.index(move.origin) + # Advance move + @players_cards[player].delete_at(@players_cards[player].index(move.destination.symbol)) + else + # Retreat move + deal_cards!(move.destination.contains.length - 1, player) + end + + # Record the new stae +# this_game_state = GameState.new(move, player, @pieces) +# @history << this_game_state + + # If this player has all their pieces in the boat, declare a win. + if @pieces[player].all? {|pc| pc.position == :boat} + raise(GameWonNotice, "Game won by #{player}") + end + end + + # Undo a move + def undo_move! + if @history.length > 1 + # general case + state_to_restore = @history[-2] + @current_player = @history[-1].player + @pieces.each do |name, piece| + copy_piece = state_to_restore.pieces_after_move[name] + piece.position = copy_piece.position + end + @history.pop + elsif @history.length == 1 + # reset to start + @current_player = @players[1] + @pieces.each do |name, piece| + piece.position = @board.positions[piece.colour] + end + @history.pop + end + end + + # Apply a list of moves in order + def apply_moves!(moves) + moves.each do |move| + if move.via_base? + moved_distance = board.distance_between[move.piece.position.place][@current_player] + + board.distance_between[@current_player][move.destination.place] + else + moved_distance = board.distance_between[move.piece.position.place][move.destination.place] + end + self.apply_move!(move, @current_player) + next_player! unless moved_distance == 6 + end + end + + + # Set the current player to be the next player + def next_player! + original_player = @current_player + if @current_player == @players[-1] + @current_player = @players[1] + else + @current_player = @players[@players.index(@current_player) + 1] + end + @current_player + end + + + # Return an array of all possible moves from this state, given the active player + def possible_moves(player = @current_player) + moves = [] + @pieces[player].each do |piece| + # Do a forward move for each card held + unless piece.position == @board.positions[-1] + @players_cards[player].each do |card| + destination = @board.positions[@board.positions.index(piece.position)..-1].find do |pos| + (pos.symbol == card and pos.contains == []) or + pos.symbol == :boat + end +# puts "Player #{player}, card #{card}, piece #{piece.number} at position #{@board.positions.index(piece.position)} to #{@board.positions.index(destination)}, a #{destination.symbol}" + moves << Move.new(piece, piece.position, destination) + end + end + # Do a reverse move for the piece + unless piece.position == board.positions[0] + destination = @board.positions[0...@board.positions.index(piece.position)].reverse.find do |pos| + pos.contains.length == 1 or pos.contains.length == 2 + end + if destination +# puts "Player #{player}, piece #{piece.number} at position #{@board.positions.index(piece.position)} retreats to #{@board.positions.index(destination)}, a #{destination.symbol} containing #{destination.contains.length} pieces" + moves << Move.new(piece, piece.position, destination) + end + end + end +# moves.each {|m| puts m.show(@board)} + moves + end + + def build_state_string + outstr = "Current player = #{@current_player}\n" + 0.upto((@board.positions.length)-1) do |i| + outstr << "#{i}: #{@board.positions[i].symbol}: " + @board.positions[i].contains.each do |piece| + outstr << "P#{piece.player}:#{piece.number} " + end + outstr << "\n" + end + 0.upto((@players.length)-1) do |i| + outstr << "Player #{i} holds " << (@players_cards[i].sort_by {|c| c.to_s}).join(', ') << "\n" + end + outstr << "Deck holds " << @deck.join(', ') << "\n" + outstr + end + + # Show the state of the board + def show_state + puts build_state_string + # @pieces.keys.sort.each do |piece_name| + # if @pieces[piece_name].captured + # puts "Piece #{piece_name} captured, at #{@pieces[piece_name].position}" + # else + # puts "Piece #{piece_name} is at #{@pieces[piece_name].position}, holds #{(@pieces[piece_name].contains.collect{|c| c.name}).join(' ')}" + # end + # end + end + + def to_s + show_state + end + + def to_str + to_s + end + + + def set_testing_game! + srand 1234 + @board = nil + initialize(6, 6, 6) + end + + # Given a set of lines from an input file, turn them into a Game object and + # a set of Move objects. + # Note the multiple return values. + # Class method + def Game.read_game(gamelines) + gamelines.each {|l| l.chomp!} + game = Game.new(gamelines[0].to_i, 6, 6, 6, 6) + moves = [] + gamelines[1..-2].each {|m| moves << m.to_move(game)} + return game, moves, gamelines[-1].to_i + end + + +end + + +# Extension to String class to convert a move-description string into a Move object. +# This is the inverse of the Move#to_s method +class String + def to_move(game) + move_elements = self.downcase.split + piece_name = move_elements[0] + destination_name = move_elements[-1] + if destination_name.length > 2 and + destination_name[-2,2] == game.board.centre.place[-2,2] + destination_name = game.board.centre.place + end + raise(InvalidMoveError, "Invalid piece in move read") unless game.pieces.has_key?(piece_name) + raise(InvalidMoveError, "Invalid destination in move read") unless game.board.positions.has_key?(destination_name) + # Deal with the synonyms for the centre position + via_base = (destination_name.length == 1 or move_elements.length > 2) + Move.new(game.pieces[piece_name], game.board.positions[destination_name], via_base) + end +end + + +# Read a game description file and convert it into a Game object and set of Move objects. +# Note that Game.read_game method returns multiple values, so this one does too. +class IO + def IO.read_game(filename) + gamelines = IO.readlines(filename) + return Game.read_game(gamelines) + end +end + +# Extension to the Array class to include the Array#shuffle function +class Array + def shuffle + sort_by { rand } + end + + def index_find(&block) + found = find(&block) + if found + index found + else + found + end + end +end diff --git a/lib/.svn/tmp/tempfile.4.tmp b/lib/.svn/tmp/tempfile.4.tmp new file mode 100644 index 0000000..6efb53c --- /dev/null +++ b/lib/.svn/tmp/tempfile.4.tmp @@ -0,0 +1,510 @@ +# == Synopsis +# +# Library to support Cartagena play +# +# == Author +# Neil Smith +# +# 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 . +# +# == Change history +# Version 1.0:: 11 Jun 2008 +# * Initial build + +# Symbols +$SYMBOLS = [:bottle, :dagger, :gun, :hat, :keys, :skull] + +# The number of cards of each symbol in the deck +$CARDS_PER_SYMBOL = 17 + +# The number of cards initiall dealt to each player +$INITIAL_CARDS_PER_PLAYER = 3 + +# Maximum number of pieces on a position +$MAX_PIECES_PER_POSITION = 3 + +# Number of actions that can be taken by each player in sequence +$MAX_MOVES_PER_TURN = 3 + +# Errors for a game + +# Moves can only be [1..6] spaces +class InvalidMoveError < StandardError +end + +# Game is won when only one player has uncaptured pieces +class GameWonNotice < StandardError +end + +# A position on the board. +class Position + attr_reader :symbol, :contains + + def initialize(symbol) + @symbol = symbol + @contains = [] + end +end + +# A tile that makes up the board. Each tile has two sides, each with six +# positions. Tiles can be either way up, and either way round. +class Tile + attr_reader :exposed, :front, :back + + def initialize(front, back) + @front = front.collect {|s| Position.new(s)} + @back = back.collect {|s| Position.new(s)} + @exposed = @front + @exposed_side = :front + end + + def flip! + if @exposed_side == :front + @exposed = @back + @exposed_side = :back + else + @exposed = @front + @exposed_side = :front + end + end + + def reverse! + @exposed = @exposed.reverse + end +end + + +# The game board +class Board + + attr_reader :positions + attr_reader :tiles + + # A laborious procedure to create all the positions and tie them all together + def initialize(tiles_used) + # A hash of all positions, indexed by position names + @tiles = [Tile.new([:hat, :keys, :gun, :bottle, :skull, :dagger], + [:hat, :keys, :gun, :bottle, :skull, :dagger]), + Tile.new([:gun, :hat, :dagger, :skull, :bottle, :keys], + [:gun, :hat, :dagger, :skull, :bottle, :keys]), + Tile.new([:skull, :gun, :bottle, :keys, :dagger, :hat], + [:skull, :gun, :bottle, :keys, :dagger, :hat]), + Tile.new([:dagger, :bottle, :keys, :gun, :hat, :skull], + [:dagger, :bottle, :keys, :gun, :hat, :skull]), + Tile.new([:keys, :dagger, :skull, :hat, :gun, :bottle], + [:keys, :dagger, :skull, :hat, :gun, :bottle]), + Tile.new([:bottle, :skull, :hat, :dagger, :keys, :gun], + [:bottle, :skull, :hat, :dagger, :keys, :gun]) + ].shuffle[0...tiles_used] + @tiles = @tiles.each do |t| + if rand < 0.5 + t.reverse! + else + t + end + end + + @positions = [Position.new(:cell)] + @tiles.each {|t| t.exposed.each {|p| @positions << p}} + @positions << Position.new(:boat) + end # def + + def to_s + layout + end + + def to_str + to_s + end + + + # For each position, show its name and what it touches + def layout + out_string = "" + @positions.each {|position| out_string << "#{position.symbol}\n"} + out_string + end + +end + + +# Each piece on the board is an object +class Piece + attr_reader :player, :number + attr_accessor :position + + def initialize(position, player, number) + @position = position + @player = player + @number = number + end + + def to_s + "#{@player}:#{@number}" + end + + def to_str + to_s + end + + def move_to(new_position) + @position = new_position + end + +end + + +# A move in a game +class Move + attr_reader :piece, :origin, :destination + + def initialize(piece, origin, destination) + @piece = piece + @origin = origin + @destination = destination + end + + def show(board) + "#{@piece.to_s}: #{board.positions.index(@origin)} -> #{board.positions.index(@destination)}" + end + + # Write a move to a string + # Note the inverse, String#to_move, is defined below + def to_s + "#{@piece.player}: #{@origin.to_s} -> #{@destination.to_s}" + end + + def to_str + to_s + end + +end + + + +# A class to record each of the states previously found in a game. +# Note that this is a deep copy of the pieces and what they've captured, so +# you'll need to convert back +class GameState + attr_accessor :move, :player, :pieces_after_move, :deck_after_move, + :players_cards_after_move, :moves_by_current_player +# this_game_state = GameState.new(move, player, @board, @pieces, @deck, @players_cards) + + + def initialize(move, player, board, pieces, deck, players_cards, moves_by_current_player) + @move = move + @player = player + @pieces_after_move = Hash.new + pieces.each {|k, p| @pieces_after_move[k] = p.dup} + end + + def ==(other) + @move.to_s == other.move.to_s and + @player == other.player and + @piece_after_move == other.pieces_after_move + end + +end + + +# A game of Cartagena. It keeps a history of all previous states. +class Game + + attr_reader :history + attr_reader :current_player, :players, :players_cards + attr_reader :moves_by_current_player + attr_reader :board + attr_reader :pieces + attr_reader :deck + + # Create a new game + def initialize(players = 6, number_of_tiles = 6, pieces_each = 6) + @board = Board.new(number_of_tiles) + @history = [] + @pieces = [] + @players = [].fill(0, players) {|i| i} + @players_cards = [] + @current_player = 0 + @moves_by_current_player = 0 + @cards = [] + 1.upto($CARDS_PER_SYMBOL) {|x| @cards.concat($SYMBOLS)} + @deck = @cards.shuffle + @players.each do |p| + @pieces[p] = [] + 0.upto(pieces_each - 1) do |count| + piece = Piece.new(@board.positions[0], p, count) + @pieces[p][count] = piece + @board.positions[0].contains << piece + end + @players_cards[p] = [] + deal_cards!($INITIAL_CARDS_PER_PLAYER, p) + end + end + + def deal_cards!(number_of_cards, player = @current_player) + 1.upto(number_of_cards) do + @deck = @cards.shuffle if @deck.empty? + @players_cards[player] << @deck.pop + end + end + + # Check that a move is valid. Throw an exception if it's invalid + def validate_move(move, player = @current_player) + # Check the move is a valid one + raise(InvalidMoveError, "Move #{move}: Player #{player} does not exist") unless @players.include?(player) + raise(InvalidMoveError, "Move #{move}: None of player #{player}'s pieces on position #{move.origin}") unless move.origin.contains.find {|pc| pc.player == player} + raise(InvalidMoveError, "Move #{move}: Origin and destination are the same") if move.origin == move.destination + + origin_position = @board.positions.index(move.origin) + destination_position = @board.positions.index(move.destination) + # Is this move an advance or a retreat? + if destination_position > origin_position + # Advancing a piece + unless @players_cards[player].find {|c| c == move.destination.symbol} + raise(InvalidMoveError, "Player #{player} does not have a card to move a piece to a #{move.destination.symbol} square") + end + # Check target square is vacant + raise(InvalidMoveError, "Advance move #{move}: destination occupied") unless move.destination.contains.empty? + # Check all the intervening squares with this symbol are occupied + intervening_empty_position = @board.positions[origin_position...destination_position].index_find do |p| + p.symbol == move.destination.symbol and + p.contains.empty? + end + raise(InvalidMoveError, "Advance move #{move}: location #{intervening_empty_position} is empty") if intervening_empty_position + else + # Retreating a piece + # Check target position has one or two pieces already on it + destination_count = move.destination.contains.length + raise(InvalidMoveError, "Retreat move #{move}: destination has no pieces already on it") if destination_count == 0 + raise(InvalidMoveError, "Retreat move #{move}: destination has too many (#{destination_count}) pieces already on it") if destination_count >= $MAX_PIECES_PER_POSITION + # Check none of the intervening squares have any pieces on them +# puts "Checking positions #{destination_position} to #{origin_position}" + intervening_target_position = @board.positions[(destination_position + 1)...origin_position].index_find do |p| +# puts "Examining postition #{p} at location #{@board.positions.index(p)} which contains #{p.contains.length} pieces" + p.contains.length > 0 and + p.contains.length < $MAX_PIECES_PER_POSITION + end + raise(InvalidMoveError, "Retreat move #{move.show(@board)}: location #{intervening_target_position} is a viable target") if intervening_target_position + end + end + + # Apply a single move to a game. + def apply_move!(move, player = @current_player) + + validate_move(move, player) + + # Apply this move + move.origin.contains.delete move.piece + move.destination.contains << move.piece + move.piece.position = move.destination + + # Update cards + if @board.positions.index(move.destination) > @board.positions.index(move.origin) + # Advance move + @players_cards[player].delete_at(@players_cards[player].index(move.destination.symbol)) + else + # Retreat move + deal_cards!(move.destination.contains.length - 1, player) + end + + # Record the new stae +# this_game_state = GameState.new(move, player, @board, @pieces, @deck, @players_cards, @moves_by_current_player) +# @history << this_game_state + + if player == @current_player + @moves_by_current_player = @moves_by_current_player + 1 + end + + # If this player has all their pieces in the boat, declare a win. + if @pieces[player].all? {|pc| pc.position == :boat} + raise(GameWonNotice, "Game won by #{player}") + end + end + + # Undo a move +# def undo_move! +# if @history.length > 1 +# # general case +# state_to_restore = @history[-2] +# @current_player = @history[-1].player +# @pieces.each do |name, piece| +# copy_piece = state_to_restore.pieces_after_move[name] +# piece.position = copy_piece.position +# end +# @history.pop +# elsif @history.length == 1 +# # reset to start +# @current_player = @players[1] +# @pieces.each do |name, piece| +# piece.position = @board.positions[piece.colour] +# end +# @history.pop +# end +# end + + # Apply a list of moves in order + def apply_moves!(moves) + moves.each do |move| + apply_move!(move, @current_player) + next_player! if @moves_by_current_player >= $MOVES_PER_TURN + end + end + + + # Set the current player to be the next player + def next_player! + original_player = @current_player + if @current_player == @players[-1] + @current_player = @players[0] + else + @current_player = @players[@players.index(@current_player) + 1] + end + @moves_by_current_player = 0 + @current_player + end + + + # Return an array of all possible moves from this state, given the active player + def possible_moves(player = @current_player) + moves = [] + @pieces[player].each do |piece| + # Do a forward move for each card held + unless piece.position == @board.positions[-1] + @players_cards[player].each do |card| + destination = @board.positions[@board.positions.index(piece.position)..-1].find do |pos| + (pos.symbol == card and pos.contains == []) or + pos.symbol == :boat + end +# puts "Player #{player}, card #{card}, piece #{piece.number} at position #{@board.positions.index(piece.position)} to #{@board.positions.index(destination)}, a #{destination.symbol}" + moves << Move.new(piece, piece.position, destination) + end + end + # Do a reverse move for the piece + unless piece.position == board.positions[0] + destination = @board.positions[0...@board.positions.index(piece.position)].reverse.find do |pos| + pos.contains.length == 1 or pos.contains.length == 2 + end + if destination +# puts "Player #{player}, piece #{piece.number} at position #{@board.positions.index(piece.position)} retreats to #{@board.positions.index(destination)}, a #{destination.symbol} containing #{destination.contains.length} pieces" + moves << Move.new(piece, piece.position, destination) + end + end + end +# moves.each {|m| puts m.show(@board)} + moves + end + + def build_state_string + outstr = "Current player = #{@current_player}\n" + 0.upto((@board.positions.length)-1) do |i| + outstr << "#{i}: #{@board.positions[i].symbol}: " + @board.positions[i].contains.each do |piece| + outstr << "P#{piece.player}:#{piece.number} " + end + outstr << "\n" + end + 0.upto((@players.length)-1) do |i| + outstr << "Player #{i} holds " << (@players_cards[i].sort_by {|c| c.to_s}).join(', ') << "\n" + end + outstr << "Deck holds " << @deck.join(', ') << "\n" + outstr + end + + # Show the state of the board + def show_state + puts build_state_string + # @pieces.keys.sort.each do |piece_name| + # if @pieces[piece_name].captured + # puts "Piece #{piece_name} captured, at #{@pieces[piece_name].position}" + # else + # puts "Piece #{piece_name} is at #{@pieces[piece_name].position}, holds #{(@pieces[piece_name].contains.collect{|c| c.name}).join(' ')}" + # end + # end + end + + def to_s + show_state + end + + def to_str + to_s + end + + + def set_testing_game! + srand 1234 + @board = nil + initialize(6, 6, 6) + end + + # Given a set of lines from an input file, turn them into a Game object and + # a set of Move objects. + # Note the multiple return values. + # Class method + def Game.read_game(gamelines) + gamelines.each {|l| l.chomp!} + game = Game.new(gamelines[0].to_i, 6, 6, 6, 6) + moves = [] + gamelines[1..-2].each {|m| moves << m.to_move(game)} + return game, moves, gamelines[-1].to_i + end + + +end + + +# Extension to String class to convert a move-description string into a Move object. +# This is the inverse of the Move#to_s method +class String + def to_move(game) + move_elements = self.downcase.split + piece_name = move_elements[0] + destination_name = move_elements[-1] + if destination_name.length > 2 and + destination_name[-2,2] == game.board.centre.place[-2,2] + destination_name = game.board.centre.place + end + raise(InvalidMoveError, "Invalid piece in move read") unless game.pieces.has_key?(piece_name) + raise(InvalidMoveError, "Invalid destination in move read") unless game.board.positions.has_key?(destination_name) + # Deal with the synonyms for the centre position + via_base = (destination_name.length == 1 or move_elements.length > 2) + Move.new(game.pieces[piece_name], game.board.positions[destination_name], via_base) + end +end + + +# Read a game description file and convert it into a Game object and set of Move objects. +# Note that Game.read_game method returns multiple values, so this one does too. +class IO + def IO.read_game(filename) + gamelines = IO.readlines(filename) + return Game.read_game(gamelines) + end +end + +# Extension to the Array class to include the Array#shuffle function +class Array + def shuffle + sort_by { rand } + end + + def index_find(&block) + found = find(&block) + if found + index found + else + found + end + end +end diff --git a/lib/.svn/tmp/tempfile.5.tmp b/lib/.svn/tmp/tempfile.5.tmp new file mode 100644 index 0000000..6ddb8d2 --- /dev/null +++ b/lib/.svn/tmp/tempfile.5.tmp @@ -0,0 +1,707 @@ +# == Synopsis +# +# Library to support Cartagena play +# +# == Author +# Neil Smith +# +# 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 . +# +# == Change history +# Version 1.0:: 11 Jun 2008 +# * Initial build + +# Symbols +$SYMBOLS = [:bottle, :dagger, :gun, :hat, :keys, :skull] + +# The number of cards of each symbol in the deck +$CARDS_PER_SYMBOL = 17 + +# The number of cards initiall dealt to each player +$INITIAL_CARDS_PER_PLAYER = 3 + +# Maximum number of pieces on a position +$MAX_PIECES_PER_POSITION = 3 + +# Number of actions that can be taken by each player in sequence +$MAX_MOVES_PER_TURN = 3 + +# Errors for a game + +# Moves can only be [1..6] spaces +class InvalidMoveError < StandardError +end + +# Game is won when only one player has uncaptured pieces +class GameWonNotice < StandardError +end + +# A position on the board. +class Position + attr_reader :symbol + attr_accessor :contains + + def initialize(symbol) + @symbol = symbol + @contains = [] + end +end + +# A tile that makes up the board. Each tile has two sides, each with six +# positions. Tiles can be either way up, and either way round. +class Tile + attr_reader :exposed, :front, :back + + def initialize(front, back) + @front = front.collect {|s| Position.new(s)} + @back = back.collect {|s| Position.new(s)} + @exposed = @front + @exposed_side = :front + end + + def flip! + if @exposed_side == :front + @exposed = @back + @exposed_side = :back + else + @exposed = @front + @exposed_side = :front + end + end + + def reverse! + @exposed = @exposed.reverse + end +end + + +# The game board +class Board + + attr_accessor :positions + attr_reader :tiles + + # A laborious procedure to create all the positions and tie them all together + def initialize(tiles_used) + # A hash of all positions, indexed by position names + @tiles = [Tile.new([:hat, :keys, :gun, :bottle, :skull, :dagger], + [:hat, :keys, :gun, :bottle, :skull, :dagger]), + Tile.new([:gun, :hat, :dagger, :skull, :bottle, :keys], + [:gun, :hat, :dagger, :skull, :bottle, :keys]), + Tile.new([:skull, :gun, :bottle, :keys, :dagger, :hat], + [:skull, :gun, :bottle, :keys, :dagger, :hat]), + Tile.new([:dagger, :bottle, :keys, :gun, :hat, :skull], + [:dagger, :bottle, :keys, :gun, :hat, :skull]), + Tile.new([:keys, :dagger, :skull, :hat, :gun, :bottle], + [:keys, :dagger, :skull, :hat, :gun, :bottle]), + Tile.new([:bottle, :skull, :hat, :dagger, :keys, :gun], + [:bottle, :skull, :hat, :dagger, :keys, :gun]) + ].shuffle[0...tiles_used] + @tiles = @tiles.each do |t| + if rand < 0.5 + t.reverse! + else + t + end + end + + @positions = [Position.new(:cell)] + @tiles.each {|t| t.exposed.each {|p| @positions << p}} + @positions << Position.new(:boat) + end # def + + def to_s + layout + end + + def to_str + to_s + end + + + # For each position, show its name and what it touches + def layout + out_string = "" + @positions.each {|position| out_string << "#{position.symbol}\n"} + out_string + end + +end + + +# Each piece on the board is an object +class Piece + attr_reader :player, :number + attr_accessor :position + + def initialize(position, player, number) + @position = position + @player = player + @number = number + end + + def to_s + "#{@player}:#{@number}" + end + + def to_str + to_s + end + + def show(convert_to_zero_based_player_number = false) + if convert_to_zero_based_player_number + "#{@player + 1}:#{@number}" + else + "#{@player}:#{@number}" + end + end + + def move_to(new_position) + @position = new_position + end + +end + + +# A move in a game +class Move + attr_reader :piece, :origin, :destination, :card_played + + def initialize(piece, origin, destination, card_played = :unspecified) + @piece = piece + @origin = origin + @destination = destination + @card_played = card_played + end + + def show(board, convert_to_zero_based_player_number = false) + if @card_played == :unspecified + "#{@piece.show(convert_to_zero_based_player_number)}: #{board.positions.index(@origin)} -> #{board.positions.index(@destination)}" + else + "#{@piece.show(convert_to_zero_based_player_number)}: #{board.positions.index(@origin)} -> #{board.positions.index(@destination)} (#{@card_played})" + end + end + + def format(board, convert_to_zero_based_player_number = false) + display_player_number = if convert_to_zero_based_player_number then + @piece.player + 1 + else + @piece.player + end + if @card_played ==:unspecified + "#{display_player_number} #{board.positions.index(@origin)} #{board.positions.index(@destination)}" + else + "#{display_player_number} #{board.positions.index(@origin)} #{board.positions.index(@destination)} #{@card_played}" + end + end + + # Write a move to a string + # Note the inverse, String#to_move, is defined below + def to_s + "#{@piece.player} #{@origin.to_s} #{@destination.to_s} #{@card_played}" + end + + def to_str + to_s + end + +end + + + +# A class to record each of the states previously found in a game. +# Note that this is a deep copy of the pieces and what they've captured, so +# you'll need to convert back +class GameState + attr_accessor :move, :player, :board, :pieces_before_move, :deck_before_move, + :players_cards_before_move, :moves_by_current_player + # this_game_state = GameState.new(move, player, @board, @pieces, @deck, @players_cards) + + + def initialize(move, player, board, pieces, deck, players_cards, moves_by_current_player) + @move, @player, @board, @pieces_before_move, @deck_before_move, + @players_cards_before_move, @moves_by_current_player = + copy_game_state(move, player, board, pieces, deck, players_cards, + moves_by_current_player) + end + + # def ==(other) + # @move.to_s == other.move.to_s and + # @player == other.player and + # @piece_after_move == other.pieces_after_move + # end + + def copy_game_state(move, player, board, pieces, deck, players_cards, moves_by_current_player) + copy_player = player + moving_player = move.piece.player + + copy_board = board.dup + copy_board.positions = board.positions.collect {|pos| pos.dup} + copy_board.positions.each {|pos| pos.contains = []} + + copy_pieces = pieces.collect do |players_pieces| + players_pieces.collect do |piece| + new_piece_position = copy_board.positions[board.positions.index(piece.position)] + new_piece = Piece.new(new_piece_position, piece.player, piece.number) + new_piece_position.contains << new_piece + new_piece + end + end + + piece_index = pieces[moving_player].index(move.piece) + origin_index = board.positions.index(move.origin) + destination_index = board.positions.index(move.destination) + copy_move = Move.new(copy_pieces[moving_player][piece_index], + copy_board.positions[origin_index], + copy_board.positions[destination_index]) + + copy_deck = deck.dup + copy_players_cards = players_cards.collect {|p| p.dup} + copy_moves_by_current_player = moves_by_current_player + + return copy_move, copy_player, copy_board, copy_pieces, copy_deck, copy_players_cards, copy_moves_by_current_player + end + +end + + +# A game of Cartagena. It keeps a history of all previous states. +class Game + + attr_reader :history + attr_accessor :current_player + attr_reader :players + attr_accessor :players_cards + attr_accessor :moves_by_current_player + attr_reader :board + attr_reader :pieces + attr_reader :cards + attr_accessor :deck + + # Create a new game + def initialize(players = 5, number_of_tiles = 6, pieces_each = 6) + @board = Board.new(number_of_tiles) + @history = [] + @pieces = [] + @players = [].fill(0, players) {|i| i} + @players_cards = [] + @current_player = 0 + @moves_by_current_player = 0 + @cards = [] + 1.upto($CARDS_PER_SYMBOL) {|x| @cards.concat($SYMBOLS)} + @deck = @cards.shuffle + @players.each do |p| + @pieces[p] = [] + 0.upto(pieces_each - 1) do |count| + piece = Piece.new(@board.positions[0], p, count) + @pieces[p][count] = piece + @board.positions[0].contains << piece + end + @players_cards[p] = [] + deal_cards!($INITIAL_CARDS_PER_PLAYER, p) + end + end + + def deal_cards!(number_of_cards, player = @current_player) + 1.upto(number_of_cards) do + if @deck.empty? + @deck = @cards + @players_cards.each do |p| + p.each do |c| + @deck.delete_at(@deck.index(c)) + end + end + @deck = @deck.shuffle + end +# @deck = @cards.shuffle if @deck.empty? + @players_cards[player] << @deck.pop + end + end + + # Check that a move is valid. Throw an exception if it's invalid + def validate_move(move, player = @current_player) + # Check the move is a valid one + raise(InvalidMoveError, "Move #{move}: Player #{player} does not exist") unless @players.include?(player) + raise(InvalidMoveError, "Move #{move}: None of player #{player}'s pieces on position #{move.origin}") unless move.origin.contains.find {|pc| pc.player == player} + raise(InvalidMoveError, "Move #{move}: Origin and destination are the same") if move.origin == move.destination + + origin_position = @board.positions.index(move.origin) + destination_position = @board.positions.index(move.destination) + # Is this move an advance or a retreat? + if destination_position > origin_position + # Advancing a piece + if move.destination == @board.positions[-1] # A move into the boat + raise(InvalidMoveError, "Move #{move}: Move into boat and card unspecified") if move.destination == @board.positions[-1] and move.card_played == :unspecified + unless @players_cards[player].find {|c| c == move.card_played} + raise(InvalidMoveError, "Player #{player} does not have a card to move a piece into the boat") + end + else + if move.card_played != :unspecified and move.destination.symbol != move.card_played + raise(InvalidMoveError, "Player #{player} trying to move to #{move.destination}, a #{move.destination.symbol} square with a a #{move.card_played} card") + end + unless @players_cards[player].find {|c| c == move.destination.symbol} + raise(InvalidMoveError, "Player #{player} does not have a card to move a piece to a #{move.destination.symbol} square") + end + # Check target square is vacant + raise(InvalidMoveError, "Advance move #{move}: destination occupied") unless move.destination.contains.empty? + end + # Check all the intervening squares with this symbol are occupied + intervening_empty_position = @board.positions[origin_position...destination_position].index_find do |p| + p.symbol == move.destination.symbol and + p.contains.empty? + end + raise(InvalidMoveError, "Advance move #{move}: location #{intervening_empty_position} is empty") if intervening_empty_position + else + # Retreating a piece + # Check target position has one or two pieces already on it + destination_count = move.destination.contains.length + raise(InvalidMoveError, "Retreat move #{move}: destination has no pieces already on it") if destination_count == 0 + raise(InvalidMoveError, "Retreat move #{move}: destination has too many (#{destination_count}) pieces already on it") if destination_count >= $MAX_PIECES_PER_POSITION + # Check none of the intervening squares have any pieces on them + # puts "Checking positions #{destination_position} to #{origin_position}" + intervening_target_position = @board.positions[(destination_position + 1)...origin_position].index_find do |p| + # puts "Examining postition #{p} at location #{@board.positions.index(p)} which contains #{p.contains.length} pieces" + p.contains.length > 0 and + p.contains.length < $MAX_PIECES_PER_POSITION + end + raise(InvalidMoveError, "Retreat move #{move.show(@board)}: location #{intervening_target_position} is a viable target") if intervening_target_position + end + end + + # Apply a single move to a game. + def apply_move!(move, player = @current_player, validate = true) + + validate_move(move, player) if validate + + raise(InvalidMoveError, "Too many consecutive moves by #{@current_player}: has taken #{@moves_by_current_player} and validate is #{validate}") if validate and player == @current_player and @moves_by_current_player >= $MAX_MOVES_PER_TURN + + # Record the old state + this_game_state = GameState.new(move, @current_player, @board, @pieces, @deck, @players_cards, @moves_by_current_player) + @history << this_game_state + + # Apply this move + move.origin.contains.delete move.piece + move.destination.contains << move.piece + move.piece.position = move.destination + + if player == @current_player + @moves_by_current_player += 1 + else + @current_player = player + @moves_by_current_player = 1 + end + + # Update cards + if @board.positions.index(move.destination) > @board.positions.index(move.origin) + # Advance move + if validate + card_to_remove = if move.card_played != :unspecified + move.card_played + else + move.destination.symbol + end + if @players_cards[player].include?(card_to_remove) + @players_cards[player].delete_at(@players_cards[player].index(card_to_remove)) + end + end + else + # Retreat move + deal_cards!(move.destination.contains.length - 1, player) + end + + # If this player has all their pieces in the boat, declare a win. + if @pieces[player].all? {|pc| pc.position == @board.positions[-1]} + raise(GameWonNotice, "Game won by #{player}") + end + end + + # Undo a move + def undo_move! + state_to_restore = @history[-1] + move, @current_player, @board, @pieces, @deck, @players_cards, + @moves_by_current_player = + state_to_restore.copy_game_state(state_to_restore.move, + state_to_restore.player, + state_to_restore.board, + state_to_restore.pieces_before_move, + state_to_restore.deck_before_move, + state_to_restore.players_cards_before_move, + state_to_restore.moves_by_current_player) + @history.pop + end + + # Apply a list of moves in order + def apply_moves!(moves) + moves.each do |move| + @current_player = move.piece.player + apply_move!(move, @current_player) + next_player! if @moves_by_current_player >= $MOVES_PER_TURN + end + end + + + # Set the current player to be the next player + def next_player! + if @current_player == @players[-1] + @current_player = @players[0] + else + @current_player = @players[@players.index(@current_player) + 1] + end + @moves_by_current_player = 0 + @current_player + end + + + def reset_current_player(new_current_player) + @current_player = new_current_player + @moves_by_current_player = 0 + end + + + # Return an array of all possible moves from this state, given the active player + def possible_moves(player = @current_player) + moves = [] + @pieces[player].each do |piece| + # Do a forward move for each card held + unless piece.position == @board.positions[-1] + @players_cards[player].each do |card| + destination = @board.positions[@board.positions.index(piece.position)..-1].find do |pos| + (pos.symbol == card and pos.contains == []) or + pos.symbol == :boat + end + # puts "Player #{player}, card #{card}, piece #{piece.number} at position #{@board.positions.index(piece.position)} to #{@board.positions.index(destination)}, a #{destination.symbol}" + moves << Move.new(piece, piece.position, destination, card) + end + end + # Do a reverse move for the piece + unless piece.position == board.positions[0] + destination = @board.positions[1...@board.positions.index(piece.position)].reverse.find do |pos| + pos.contains.length == 1 or pos.contains.length == 2 + end + if destination + # puts "Player #{player}, piece #{piece.number} at position #{@board.positions.index(piece.position)} retreats to #{@board.positions.index(destination)}, a #{destination.symbol} containing #{destination.contains.length} pieces" + moves << Move.new(piece, piece.position, destination) + end + end + end + # moves.each {|m| puts m.show(@board)} + moves + end + + def build_state_string + outstr = "Current player = #{@current_player}\n" + 0.upto((@board.positions.length)-1) do |i| + outstr << "#{i}: #{@board.positions[i].symbol}: " + @board.positions[i].contains.each do |piece| + outstr << "P#{piece.player}:#{piece.number} " + end + outstr << "\n" + end + 0.upto((@players.length)-1) do |i| + outstr << "Player #{i} holds " << (@players_cards[i].sort_by {|c| c.to_s}).join(', ') << "\n" + end + outstr << "Deck holds " << @deck.join(', ') << "\n" + outstr + end + + # Show the state of the board + def show_state + puts build_state_string + # @pieces.keys.sort.each do |piece_name| + # if @pieces[piece_name].captured + # puts "Piece #{piece_name} captured, at #{@pieces[piece_name].position}" + # else + # puts "Piece #{piece_name} is at #{@pieces[piece_name].position}, holds #{(@pieces[piece_name].contains.collect{|c| c.name}).join(' ')}" + # end + # end + end + + def to_s + show_state + end + + def to_str + to_s + end + + + def set_testing_game! + srand 1234 + @board = nil + initialize(5, 6, 6) + end + + # Given a set of lines from an input file, turn them into a Game object and + # a set of Move objects. + # Note the multiple return values. + # Class method + def Game.read_game(gamelines) + gamelines.each {|l| l.chomp!} +# puts "Creating game with #{gamelines[0].to_i} players" + game = Game.new(gamelines[0].to_i, 6, 6) + + # create the board + 2.upto(38) do |i| + game.board.positions[i-1] = Position.new(gamelines[i].to_sym) +# puts "Making board position #{i-1} a #{gamelines[i].to_sym}" + end + + # read all the moves so far + moves = [] + i = 39 + current_player_moves = 0 + current_player = 0 +<<<<<<< .mine + player_card_counts = game.players_cards.map{|p| 3} +# player_card_counts.each {|i| i = 3} +# unused_cards = game.cards.map{|c| c} +======= + player_card_counts = game.players_cards.map {|p| $INITIAL_CARDS_PER_PLAYER} + unused_cards = game.cards.map {|c| c} +>>>>>>> .r49 + while gamelines[i].in_move_format? + move = gamelines[i].to_move(game, true) +# puts "Applying move #{move.show(game.board)} from line #{i} of the file. Current player before move application is #{game.current_player}" + if move.piece.player == current_player + current_player_moves += 1 + else + current_player = move.piece.player + current_player_moves = 1 + end + # if move is an advance move, decrement the cards held by the player and remove the card from unused cards + # throw an error if an advance move is made when all the cards of that type have been used + # if unused_cards becomes empty, restock it. + if game.board.positions.index(move.destination) > game.board.positions.index(move.origin) + raise(InvalidMoveError, "Game reading: Player #{move.piece.player} attempting an advance move with no cards in hand. Move is #{gamelines[i]} on line #{i}") if player_card_counts[move.piece.player] == 0 + raise(InvalidMoveError, "Game reading: Player #{move.piece.player} attempting an advance move to a #{move.destination.symbol} place when all those cards have been used. Move is #{gamelines[i]} on line #{i}") unless unused_cards.include? move.destination.symbol + player_card_counts[current_player] -= 1 + unused_cards.delete_at(unused_cards.index(move.destination.symbol)) + # if move is a retreat move, increment the cards held by the player + else + player_card_counts[current_player] += move.destination.contains.length + if unused_cards.length == player_card_counts.inject {|sum, n| sum + n } + unused_cards = game.cards.map {|c| c} + end + end + game.apply_move!(move, move.piece.player, false) + moves << move +# puts "Applied move #{move.show(game.board)} from line #{i} of the file" + i = i + 1 + end + + # note the current player + game.current_player = gamelines[i].to_i - 1 + if game.current_player == current_player + game.moves_by_current_player = current_player_moves + else + game.moves_by_current_player = 0 + end + puts "Setting current player to #{game.current_player} on line #{i}" + current_player = game.current_player + + # read the cards + game.players_cards = game.players_cards.map {|c| []} +player_card_counts.each_index {|index| puts "#{index} should have #{player_card_counts[index]} cards"} +game.players_cards.each_index {|index| puts "#{index} has #{game.players_cards[index]} cards"} +puts "Current player is #{game.current_player} whose cards are #{game.players_cards[game.current_player]}" +# game.players_cards[game.current_player] = [] + (i+1).upto(gamelines.length - 1) do |j| + game.players_cards[game.current_player] << gamelines[j].to_sym + unused_cards.delete_at(unused_cards.index(move.destination.symbol)) + puts "Reading line #{j} to give player #{game.current_player} a #{gamelines[j].to_sym} card" + end + raise(InvalidMoveError, "Player #{game.current_player} given #{game.players_cards[game.current_player].length} cards, but should have #{player_card_counts[game.current_player]} cards from play") if game.players_cards[game.current_player].length != player_card_counts[game.current_player] + + # Update the game deck + +puts "#{unused_cards.length} unused cards" +player_card_counts.each {|c| puts "has #{c} cards"} + unused_cards = unused_cards.shuffle + player_card_counts[game.current_player] = 0 + player_card_counts.each_index do |player| + if player_card_counts[player] > 0 + game.deal_cards!(player_card_counts[player], player) +# 1.upto(count) {unused_cards.pop} + end + end + game.deck = unused_cards +puts "#{game.deck.length} cards remaining in deck" +game.players_cards.each {|cards| puts "holds #{cards}"} + return game, moves + end + +end + + +# Extension to String class to convert a move-description string into a Move object. +# This is the inverse of the Move#to_s method +class String + def in_move_format? + elements = split + return ((elements.length == 3 or elements.length == 4) and + elements[0].to_i > 0 and elements[0].to_i < 6 and + elements[1].to_i >= 0 and elements[1].to_i <= 37 and + elements[2].to_i >= 0 and elements[2].to_i <= 37) + end + + def to_move(game, convert_to_zero_based_player_number = false) + move_elements = self.downcase.split + player = move_elements[0].to_i + player = player - 1 if convert_to_zero_based_player_number + origin_index = move_elements[1].to_i + destination_index = move_elements[2].to_i + raise(InvalidMoveError, "Invalid origin #{origin_index} in move read") unless origin_index >= 0 and origin_index < game.board.positions.length + raise(InvalidMoveError, "Invalid destination #{destination_index} in move read") unless destination_index > 0 and destination_index <= game.board.positions.length + raise(InvalidMoveError, "Player #{player} does not have a piece on position #{origin_index} in move read") unless game.board.positions[origin_index].contains.any? {|p| p.player == player} + piece = game.board.positions[origin_index].contains.find {|p| p.player == player} + piece_name = piece.number + if move_elements.length == 4 + raise(InvalidMoveError, "Invalid card played: #{move_elements[3]}") unless $SYMBOLS.include?(move_elements[3].chomp.to_sym) + Move.new(game.pieces[player][piece_name], + game.board.positions[origin_index], + game.board.positions[destination_index], + move_elements[3].chomp.to_sym) + else + Move.new(game.pieces[player][piece_name], + game.board.positions[origin_index], + game.board.positions[destination_index]) + end + end +end + + +# Read a game description file and convert it into a Game object and set of Move objects. +# Note that Game.read_game method returns multiple values, so this one does too. +class IO + def IO.read_game(filename) + gamelines = IO.readlines(filename) + return Game.read_game(gamelines) + end +end + +# Extension to the Array class to include the Array#shuffle function +class Array + def shuffle + sort_by { rand } + end + + def index_find(&block) + found = find(&block) + if found + index found + else + found + end + end +end diff --git a/lib/.svn/tmp/tempfile.6.tmp b/lib/.svn/tmp/tempfile.6.tmp new file mode 100644 index 0000000..a881e56 --- /dev/null +++ b/lib/.svn/tmp/tempfile.6.tmp @@ -0,0 +1,704 @@ +# == Synopsis +# +# Library to support Cartagena play +# +# == Author +# Neil Smith +# +# 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 . +# +# == Change history +# Version 1.0:: 11 Jun 2008 +# * Initial build + +# Symbols +$SYMBOLS = [:bottle, :dagger, :gun, :hat, :keys, :skull] + +# The number of cards of each symbol in the deck +$CARDS_PER_SYMBOL = 17 + +# The number of cards initiall dealt to each player +$INITIAL_CARDS_PER_PLAYER = 3 + +# Maximum number of pieces on a position +$MAX_PIECES_PER_POSITION = 3 + +# Number of actions that can be taken by each player in sequence +$MAX_MOVES_PER_TURN = 3 + +# Errors for a game + +# Moves can only be [1..6] spaces +class InvalidMoveError < StandardError +end + +# Game is won when only one player has uncaptured pieces +class GameWonNotice < StandardError +end + +# A position on the board. +class Position + attr_reader :symbol + attr_accessor :contains + + def initialize(symbol) + @symbol = symbol + @contains = [] + end +end + +# A tile that makes up the board. Each tile has two sides, each with six +# positions. Tiles can be either way up, and either way round. +class Tile + attr_reader :exposed, :front, :back + + def initialize(front, back) + @front = front.collect {|s| Position.new(s)} + @back = back.collect {|s| Position.new(s)} + @exposed = @front + @exposed_side = :front + end + + def flip! + if @exposed_side == :front + @exposed = @back + @exposed_side = :back + else + @exposed = @front + @exposed_side = :front + end + end + + def reverse! + @exposed = @exposed.reverse + end +end + + +# The game board +class Board + + attr_accessor :positions + attr_reader :tiles + + # A laborious procedure to create all the positions and tie them all together + def initialize(tiles_used) + # A hash of all positions, indexed by position names + @tiles = [Tile.new([:hat, :keys, :gun, :bottle, :skull, :dagger], + [:hat, :keys, :gun, :bottle, :skull, :dagger]), + Tile.new([:gun, :hat, :dagger, :skull, :bottle, :keys], + [:gun, :hat, :dagger, :skull, :bottle, :keys]), + Tile.new([:skull, :gun, :bottle, :keys, :dagger, :hat], + [:skull, :gun, :bottle, :keys, :dagger, :hat]), + Tile.new([:dagger, :bottle, :keys, :gun, :hat, :skull], + [:dagger, :bottle, :keys, :gun, :hat, :skull]), + Tile.new([:keys, :dagger, :skull, :hat, :gun, :bottle], + [:keys, :dagger, :skull, :hat, :gun, :bottle]), + Tile.new([:bottle, :skull, :hat, :dagger, :keys, :gun], + [:bottle, :skull, :hat, :dagger, :keys, :gun]) + ].shuffle[0...tiles_used] + @tiles = @tiles.each do |t| + if rand < 0.5 + t.reverse! + else + t + end + end + + @positions = [Position.new(:cell)] + @tiles.each {|t| t.exposed.each {|p| @positions << p}} + @positions << Position.new(:boat) + end # def + + def to_s + layout + end + + def to_str + to_s + end + + + # For each position, show its name and what it touches + def layout + out_string = "" + @positions.each {|position| out_string << "#{position.symbol}\n"} + out_string + end + +end + + +# Each piece on the board is an object +class Piece + attr_reader :player, :number + attr_accessor :position + + def initialize(position, player, number) + @position = position + @player = player + @number = number + end + + def to_s + "#{@player}:#{@number}" + end + + def to_str + to_s + end + + def show(convert_to_zero_based_player_number = false) + if convert_to_zero_based_player_number + "#{@player + 1}:#{@number}" + else + "#{@player}:#{@number}" + end + end + + def move_to(new_position) + @position = new_position + end + +end + + +# A move in a game +class Move + attr_reader :piece, :origin, :destination, :card_played + + def initialize(piece, origin, destination, card_played = :unspecified) + @piece = piece + @origin = origin + @destination = destination + @card_played = card_played + end + + def show(board, convert_to_zero_based_player_number = false) + if @card_played == :unspecified + "#{@piece.show(convert_to_zero_based_player_number)}: #{board.positions.index(@origin)} -> #{board.positions.index(@destination)}" + else + "#{@piece.show(convert_to_zero_based_player_number)}: #{board.positions.index(@origin)} -> #{board.positions.index(@destination)} (#{@card_played})" + end + end + + def format(board, convert_to_zero_based_player_number = false) + display_player_number = if convert_to_zero_based_player_number then + @piece.player + 1 + else + @piece.player + end + if @card_played ==:unspecified + "#{display_player_number} #{board.positions.index(@origin)} #{board.positions.index(@destination)}" + else + "#{display_player_number} #{board.positions.index(@origin)} #{board.positions.index(@destination)} #{@card_played}" + end + end + + # Write a move to a string + # Note the inverse, String#to_move, is defined below + def to_s + "#{@piece.player} #{@origin.to_s} #{@destination.to_s} #{@card_played}" + end + + def to_str + to_s + end + +end + + + +# A class to record each of the states previously found in a game. +# Note that this is a deep copy of the pieces and what they've captured, so +# you'll need to convert back +class GameState + attr_accessor :move, :player, :board, :pieces_before_move, :deck_before_move, + :players_cards_before_move, :moves_by_current_player + # this_game_state = GameState.new(move, player, @board, @pieces, @deck, @players_cards) + + + def initialize(move, player, board, pieces, deck, players_cards, moves_by_current_player) + @move, @player, @board, @pieces_before_move, @deck_before_move, + @players_cards_before_move, @moves_by_current_player = + copy_game_state(move, player, board, pieces, deck, players_cards, + moves_by_current_player) + end + + # def ==(other) + # @move.to_s == other.move.to_s and + # @player == other.player and + # @piece_after_move == other.pieces_after_move + # end + + def copy_game_state(move, player, board, pieces, deck, players_cards, moves_by_current_player) + copy_player = player + moving_player = move.piece.player + + copy_board = board.dup + copy_board.positions = board.positions.collect {|pos| pos.dup} + copy_board.positions.each {|pos| pos.contains = []} + + copy_pieces = pieces.collect do |players_pieces| + players_pieces.collect do |piece| + new_piece_position = copy_board.positions[board.positions.index(piece.position)] + new_piece = Piece.new(new_piece_position, piece.player, piece.number) + new_piece_position.contains << new_piece + new_piece + end + end + + piece_index = pieces[moving_player].index(move.piece) + origin_index = board.positions.index(move.origin) + destination_index = board.positions.index(move.destination) + copy_move = Move.new(copy_pieces[moving_player][piece_index], + copy_board.positions[origin_index], + copy_board.positions[destination_index]) + + copy_deck = deck.dup + copy_players_cards = players_cards.collect {|p| p.dup} + copy_moves_by_current_player = moves_by_current_player + + return copy_move, copy_player, copy_board, copy_pieces, copy_deck, copy_players_cards, copy_moves_by_current_player + end + +end + + +# A game of Cartagena. It keeps a history of all previous states. +class Game + + attr_reader :history + attr_accessor :current_player + attr_reader :players + attr_accessor :players_cards + attr_accessor :moves_by_current_player + attr_reader :board + attr_reader :pieces + attr_reader :cards + attr_accessor :deck + + # Create a new game + def initialize(players = 5, number_of_tiles = 6, pieces_each = 6) + @board = Board.new(number_of_tiles) + @history = [] + @pieces = [] + @players = [].fill(0, players) {|i| i} + @players_cards = [] + @current_player = 0 + @moves_by_current_player = 0 + @cards = [] + 1.upto($CARDS_PER_SYMBOL) {|x| @cards.concat($SYMBOLS)} + @deck = @cards.shuffle + @players.each do |p| + @pieces[p] = [] + 0.upto(pieces_each - 1) do |count| + piece = Piece.new(@board.positions[0], p, count) + @pieces[p][count] = piece + @board.positions[0].contains << piece + end + @players_cards[p] = [] + deal_cards!($INITIAL_CARDS_PER_PLAYER, p) + end + end + + def deal_cards!(number_of_cards, player = @current_player) + 1.upto(number_of_cards) do + if @deck.empty? + @deck = @cards + @players_cards.each do |p| + p.each do |c| + @deck.delete_at(@deck.index(c)) + end + end + @deck = @deck.shuffle + end +# @deck = @cards.shuffle if @deck.empty? + @players_cards[player] << @deck.pop + end + end + + # Check that a move is valid. Throw an exception if it's invalid + def validate_move(move, player = @current_player) + # Check the move is a valid one + raise(InvalidMoveError, "Move #{move}: Player #{player} does not exist") unless @players.include?(player) + raise(InvalidMoveError, "Move #{move}: None of player #{player}'s pieces on position #{move.origin}") unless move.origin.contains.find {|pc| pc.player == player} + raise(InvalidMoveError, "Move #{move}: Origin and destination are the same") if move.origin == move.destination + + origin_position = @board.positions.index(move.origin) + destination_position = @board.positions.index(move.destination) + # Is this move an advance or a retreat? + if destination_position > origin_position + # Advancing a piece + if move.destination == @board.positions[-1] # A move into the boat + raise(InvalidMoveError, "Move #{move}: Move into boat and card unspecified") if move.destination == @board.positions[-1] and move.card_played == :unspecified + unless @players_cards[player].find {|c| c == move.card_played} + raise(InvalidMoveError, "Player #{player} does not have a card to move a piece into the boat") + end + else + if move.card_played != :unspecified and move.destination.symbol != move.card_played + raise(InvalidMoveError, "Player #{player} trying to move to #{move.destination}, a #{move.destination.symbol} square with a a #{move.card_played} card") + end + unless @players_cards[player].find {|c| c == move.destination.symbol} + raise(InvalidMoveError, "Player #{player} does not have a card to move a piece to a #{move.destination.symbol} square") + end + # Check target square is vacant + raise(InvalidMoveError, "Advance move #{move}: destination occupied") unless move.destination.contains.empty? + end + # Check all the intervening squares with this symbol are occupied + intervening_empty_position = @board.positions[origin_position...destination_position].index_find do |p| + p.symbol == move.destination.symbol and + p.contains.empty? + end + raise(InvalidMoveError, "Advance move #{move}: location #{intervening_empty_position} is empty") if intervening_empty_position + else + # Retreating a piece + # Check target position has one or two pieces already on it + destination_count = move.destination.contains.length + raise(InvalidMoveError, "Retreat move #{move}: destination has no pieces already on it") if destination_count == 0 + raise(InvalidMoveError, "Retreat move #{move}: destination has too many (#{destination_count}) pieces already on it") if destination_count >= $MAX_PIECES_PER_POSITION + # Check none of the intervening squares have any pieces on them + # puts "Checking positions #{destination_position} to #{origin_position}" + intervening_target_position = @board.positions[(destination_position + 1)...origin_position].index_find do |p| + # puts "Examining postition #{p} at location #{@board.positions.index(p)} which contains #{p.contains.length} pieces" + p.contains.length > 0 and + p.contains.length < $MAX_PIECES_PER_POSITION + end + raise(InvalidMoveError, "Retreat move #{move.show(@board)}: location #{intervening_target_position} is a viable target") if intervening_target_position + end + end + + # Apply a single move to a game. + def apply_move!(move, player = @current_player, validate = true) + + validate_move(move, player) if validate + + raise(InvalidMoveError, "Too many consecutive moves by #{@current_player}: has taken #{@moves_by_current_player} and validate is #{validate}") if validate and player == @current_player and @moves_by_current_player >= $MAX_MOVES_PER_TURN + + # Record the old state + this_game_state = GameState.new(move, @current_player, @board, @pieces, @deck, @players_cards, @moves_by_current_player) + @history << this_game_state + + # Apply this move + move.origin.contains.delete move.piece + move.destination.contains << move.piece + move.piece.position = move.destination + + if player == @current_player + @moves_by_current_player += 1 + else + @current_player = player + @moves_by_current_player = 1 + end + + # Update cards + if @board.positions.index(move.destination) > @board.positions.index(move.origin) + # Advance move + if validate + card_to_remove = if move.card_played != :unspecified + move.card_played + else + move.destination.symbol + end + if @players_cards[player].include?(card_to_remove) + @players_cards[player].delete_at(@players_cards[player].index(card_to_remove)) + end + end + else + # Retreat move + deal_cards!(move.destination.contains.length - 1, player) + end + + # If this player has all their pieces in the boat, declare a win. + if @pieces[player].all? {|pc| pc.position == @board.positions[-1]} + raise(GameWonNotice, "Game won by #{player}") + end + end + + # Undo a move + def undo_move! + state_to_restore = @history[-1] + move, @current_player, @board, @pieces, @deck, @players_cards, + @moves_by_current_player = + state_to_restore.copy_game_state(state_to_restore.move, + state_to_restore.player, + state_to_restore.board, + state_to_restore.pieces_before_move, + state_to_restore.deck_before_move, + state_to_restore.players_cards_before_move, + state_to_restore.moves_by_current_player) + @history.pop + end + + # Apply a list of moves in order + def apply_moves!(moves) + moves.each do |move| + @current_player = move.piece.player + apply_move!(move, @current_player) + next_player! if @moves_by_current_player >= $MOVES_PER_TURN + end + end + + + # Set the current player to be the next player + def next_player! + if @current_player == @players[-1] + @current_player = @players[0] + else + @current_player = @players[@players.index(@current_player) + 1] + end + @moves_by_current_player = 0 + @current_player + end + + + def reset_current_player(new_current_player) + @current_player = new_current_player + @moves_by_current_player = 0 + end + + + # Return an array of all possible moves from this state, given the active player + def possible_moves(player = @current_player) + moves = [] + @pieces[player].each do |piece| + # Do a forward move for each card held + unless piece.position == @board.positions[-1] + @players_cards[player].each do |card| + destination = @board.positions[@board.positions.index(piece.position)..-1].find do |pos| + (pos.symbol == card and pos.contains == []) or + pos.symbol == :boat + end + # puts "Player #{player}, card #{card}, piece #{piece.number} at position #{@board.positions.index(piece.position)} to #{@board.positions.index(destination)}, a #{destination.symbol}" + moves << Move.new(piece, piece.position, destination, card) + end + end + # Do a reverse move for the piece + unless piece.position == board.positions[0] + destination = @board.positions[1...@board.positions.index(piece.position)].reverse.find do |pos| + pos.contains.length == 1 or pos.contains.length == 2 + end + if destination + # puts "Player #{player}, piece #{piece.number} at position #{@board.positions.index(piece.position)} retreats to #{@board.positions.index(destination)}, a #{destination.symbol} containing #{destination.contains.length} pieces" + moves << Move.new(piece, piece.position, destination) + end + end + end + # moves.each {|m| puts m.show(@board)} + moves + end + + def build_state_string + outstr = "Current player = #{@current_player}\n" + 0.upto((@board.positions.length)-1) do |i| + outstr << "#{i}: #{@board.positions[i].symbol}: " + @board.positions[i].contains.each do |piece| + outstr << "P#{piece.player}:#{piece.number} " + end + outstr << "\n" + end + 0.upto((@players.length)-1) do |i| + outstr << "Player #{i} holds " << (@players_cards[i].sort_by {|c| c.to_s}).join(', ') << "\n" + end + outstr << "Deck holds " << @deck.join(', ') << "\n" + outstr + end + + # Show the state of the board + def show_state + puts build_state_string + # @pieces.keys.sort.each do |piece_name| + # if @pieces[piece_name].captured + # puts "Piece #{piece_name} captured, at #{@pieces[piece_name].position}" + # else + # puts "Piece #{piece_name} is at #{@pieces[piece_name].position}, holds #{(@pieces[piece_name].contains.collect{|c| c.name}).join(' ')}" + # end + # end + end + + def to_s + show_state + end + + def to_str + to_s + end + + + def set_testing_game! + srand 1234 + @board = nil + initialize(5, 6, 6) + end + + # Given a set of lines from an input file, turn them into a Game object and + # a set of Move objects. + # Note the multiple return values. + # Class method + def Game.read_game(gamelines) + gamelines.each {|l| l.chomp!} +# puts "Creating game with #{gamelines[0].to_i} players" + game = Game.new(gamelines[0].to_i, 6, 6) + + # create the board + 2.upto(38) do |i| + game.board.positions[i-1] = Position.new(gamelines[i].to_sym) +# puts "Making board position #{i-1} a #{gamelines[i].to_sym}" + end + + # read all the moves so far + moves = [] + i = 39 + current_player_moves = 0 + current_player = 0 +<<<<<<< .mine + player_card_counts = game.players_cards.map{|p| 3} +# player_card_counts.each {|i| i = 3} +# unused_cards = game.cards.map{|c| c} +======= + player_card_counts = game.players_cards.map {|p| $INITIAL_CARDS_PER_PLAYER} + unused_cards = game.cards.map {|c| c} +>>>>>>> .r49 + while gamelines[i].in_move_format? + move = gamelines[i].to_move(game, true) +# puts "Applying move #{move.show(game.board)} from line #{i} of the file. Current player before move application is #{game.current_player}" + if move.piece.player == current_player + current_player_moves += 1 + else + current_player = move.piece.player + current_player_moves = 1 + end + # if move is an advance move, decrement the cards held by the player and remove the card from unused cards + # throw an error if an advance move is made when all the cards of that type have been used + # if unused_cards becomes empty, restock it. + if game.board.positions.index(move.destination) > game.board.positions.index(move.origin) + raise(InvalidMoveError, "Game reading: Player #{move.piece.player} attempting an advance move with no cards in hand. Move is #{gamelines[i]} on line #{i}") if player_card_counts[move.piece.player] == 0 + raise(InvalidMoveError, "Game reading: Player #{move.piece.player} attempting an advance move to a #{move.destination.symbol} place when all those cards have been used. Move is #{gamelines[i]} on line #{i}") unless unused_cards.include? move.destination.symbol + player_card_counts[current_player] -= 1 + unused_cards.delete_at(unused_cards.index(move.destination.symbol)) + # if move is a retreat move, increment the cards held by the player + else + player_card_counts[current_player] += move.destination.contains.length + if unused_cards.length == player_card_counts.inject {|sum, n| sum + n } + unused_cards = game.cards.map {|c| c} + end + end + game.apply_move!(move, move.piece.player, false) + moves << move +# puts "Applied move #{move.show(game.board)} from line #{i} of the file" + i = i + 1 + end + + # note the current player + game.current_player = gamelines[i].to_i - 1 + if game.current_player == current_player + game.moves_by_current_player = current_player_moves + else + game.moves_by_current_player = 0 + end +# puts "Setting current player to #{game.current_player} on line #{i}" + current_player = game.current_player + + # read the cards + game.players_cards = game.players_cards.map {|c| []} +# player_card_counts.each_index {|index| puts "#{index} should have #{player_card_counts[index]} cards"} +# game.players_cards.each_index {|index| puts "#{index} has #{game.players_cards[index]} cards"} +# puts "Current player is #{game.current_player} whose cards are #{game.players_cards[game.current_player]}" + (i+1).upto(gamelines.length - 1) do |j| + game.players_cards[game.current_player] << gamelines[j].to_sym + unused_cards.delete_at(unused_cards.index(move.destination.symbol)) +# puts "Reading line #{j} to give player #{game.current_player} a #{gamelines[j].to_sym} card" + end + raise(InvalidMoveError, "Player #{game.current_player} given #{game.players_cards[game.current_player].length} cards, but should have #{player_card_counts[game.current_player]} cards from play") if game.players_cards[game.current_player].length != player_card_counts[game.current_player] + + # Update the game deck + +# puts "#{unused_cards.length} unused cards" +# player_card_counts.each {|c| puts "has #{c} cards"} + game.deck = unused_cards.shuffle + player_card_counts[game.current_player] = 0 + player_card_counts.each_index do |player| + if player_card_counts[player] > 0 + game.deal_cards!(player_card_counts[player], player) + end + end +# puts "#{game.deck.length} cards remaining in deck" +# game.players_cards.each {|cards| puts "holds #{cards}"} + return game, moves + end + +end + + +# Extension to String class to convert a move-description string into a Move object. +# This is the inverse of the Move#to_s method +class String + def in_move_format? + elements = split + return ((elements.length == 3 or elements.length == 4) and + elements[0].to_i > 0 and elements[0].to_i < 6 and + elements[1].to_i >= 0 and elements[1].to_i <= 37 and + elements[2].to_i >= 0 and elements[2].to_i <= 37) + end + + def to_move(game, convert_to_zero_based_player_number = false) + move_elements = self.downcase.split + player = move_elements[0].to_i + player = player - 1 if convert_to_zero_based_player_number + origin_index = move_elements[1].to_i + destination_index = move_elements[2].to_i + raise(InvalidMoveError, "Invalid origin #{origin_index} in move read") unless origin_index >= 0 and origin_index < game.board.positions.length + raise(InvalidMoveError, "Invalid destination #{destination_index} in move read") unless destination_index > 0 and destination_index <= game.board.positions.length + raise(InvalidMoveError, "Player #{player} does not have a piece on position #{origin_index} in move read") unless game.board.positions[origin_index].contains.any? {|p| p.player == player} + piece = game.board.positions[origin_index].contains.find {|p| p.player == player} + piece_name = piece.number + if move_elements.length == 4 + raise(InvalidMoveError, "Invalid card played: #{move_elements[3]}") unless $SYMBOLS.include?(move_elements[3].chomp.to_sym) + Move.new(game.pieces[player][piece_name], + game.board.positions[origin_index], + game.board.positions[destination_index], + move_elements[3].chomp.to_sym) + else + Move.new(game.pieces[player][piece_name], + game.board.positions[origin_index], + game.board.positions[destination_index]) + end + end +end + + +# Read a game description file and convert it into a Game object and set of Move objects. +# Note that Game.read_game method returns multiple values, so this one does too. +class IO + def IO.read_game(filename) + gamelines = IO.readlines(filename) + return Game.read_game(gamelines) + end +end + +# Extension to the Array class to include the Array#shuffle function +class Array + def shuffle + sort_by { rand } + end + + def index_find(&block) + found = find(&block) + if found + index found + else + found + end + end +end diff --git a/lib/.svn/tmp/tempfile.tmp b/lib/.svn/tmp/tempfile.tmp new file mode 100644 index 0000000..572ef8e --- /dev/null +++ b/lib/.svn/tmp/tempfile.tmp @@ -0,0 +1,508 @@ +# == Synopsis +# +# Library to support Cartagena play +# +# == Author +# Neil Smith +# +# 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 . +# +# == Change history +# Version 1.0:: 11 Jun 2008 +# * Initial build + +# Symbols +$SYMBOLS = [:bottle, :dagger, :gun, :hat, :keys, :skull] + +# The number of cards of each symbol in the deck +$CARDS_PER_SYMBOL = 17 + +# The number of cards initiall dealt to each player +$INITIAL_CARDS_PER_PLAYER = 3 + +# Maximum number of pieces on a position +$MAX_PIECES_PER_POSITION = 3 + +# Errors for a game + +# Moves can only be [1..6] spaces +class InvalidMoveError < StandardError +end + +# Game is won when only one player has uncaptured pieces +class GameWonNotice < StandardError +end + +# A position on the board. +class Position + attr_reader :symbol, :contains + + def initialize(symbol) + @symbol = symbol + @contains = [] + end +end + +# A tile that makes up the board. Each tile has two sides, each with six +# positions. Tiles can be either way up, and either way round. +class Tile + attr_reader :exposed, :front, :back + + def initialize(front, back) + @front = front.collect {|s| Position.new(s)} + @back = back.collect {|s| Position.new(s)} + @exposed = @front + @exposed_side = :front + end + + def flip! + if @exposed_side == :front + @exposed = @back + @exposed_side = :back + else + @exposed = @front + @exposed_side = :front + end + end + + def reverse! + @exposed = @exposed.reverse + end +end + + +# The game board +class Board + + attr_reader :positions + attr_reader :tiles + + # A laborious procedure to create all the positions and tie them all together + def initialize(tiles_used) + # A hash of all positions, indexed by position names + @tiles = [Tile.new([:hat, :keys, :gun, :bottle, :skull, :dagger], + [:hat, :keys, :gun, :bottle, :skull, :dagger]), + Tile.new([:gun, :hat, :dagger, :skull, :bottle, :keys], + [:gun, :hat, :dagger, :skull, :bottle, :keys]), + Tile.new([:skull, :gun, :bottle, :keys, :dagger, :hat], + [:skull, :gun, :bottle, :keys, :dagger, :hat]), + Tile.new([:dagger, :bottle, :keys, :gun, :hat, :skull], + [:dagger, :bottle, :keys, :gun, :hat, :skull]), + Tile.new([:keys, :dagger, :skull, :hat, :gun, :bottle], + [:keys, :dagger, :skull, :hat, :gun, :bottle]), + Tile.new([:bottle, :skull, :hat, :dagger, :keys, :gun], + [:bottle, :skull, :hat, :dagger, :keys, :gun]) + ].shuffle[0...tiles_used] + @tiles = @tiles.each do |t| + if rand < 0.5 + t.reverse! + else + t + end + end + + @positions = [Position.new(:cell)] + @tiles.each {|t| t.exposed.each {|p| @positions << p}} + @positions << Position.new(:boat) + end # def + + def to_s + layout + end + + def to_str + to_s + end + + + # For each position, show its name and what it touches + def layout + out_string = "" + @positions.each {|position| out_string << "#{position.symbol}\n"} + out_string + end + +end + + +# Each piece on the board is an object +class Piece + attr_reader :player, :number + attr_accessor :position + + def initialize(position, player, number) + @position = position + @player = player + @number = number + end + + def to_s + "#{@player}:#{@number}" + end + + def to_str + to_s + end + + def move_to(new_position) + @position = new_position + end + +end + + +# A move in a game +class Move + attr_reader :piece, :origin, :destination + + def initialize(piece, origin, destination) + @piece = piece + @origin = origin + @destination = destination + end + + def show(board) + "#{@piece.to_s}: #{board.positions.index(@origin)} -> #{board.positions.index(@destination)}" + end + + # Write a move to a string + # Note the inverse, String#to_move, is defined below + def to_s + "#{@piece.player}: #{@origin.to_s} -> #{@destination.to_s}" + end + + def to_str + to_s + end + +end + + + +# A class to record each of the states previously found in a game. +# Note that this is a deep copy of the pieces and what they've captured, so +# you'll need to convert back +class GameState + attr_accessor :move, :player, :pieces_after_move + + def initialize(move, player, pieces) + @move = move + @player = player + @pieces_after_move = Hash.new + pieces.each {|k, p| @pieces_after_move[k] = p.dup} + end + + def ==(other) + @move.to_s == other.move.to_s and + @player == other.player and + @piece_after_move == other.pieces_after_move + end + +end + + +# A game of Cartagena. It keeps a history of all previous states. +class Game + + attr_reader :history + attr_reader :current_player, :players, :players_cards + attr_reader :board + attr_reader :pieces + attr_reader :deck + + # Create a new game + def initialize(players = 6, number_of_tiles = 6, pieces_each = 6) + @board = Board.new(number_of_tiles) + @history = [] + @pieces = [] + @players = [].fill(0, players) {|i| i} + @players_cards = [] + @current_player = 0 + @cards = [] + 1.upto($CARDS_PER_SYMBOL) {|x| @cards.concat($SYMBOLS)} + @deck = @cards.shuffle + @players.each do |p| + @pieces[p] = [] + 0.upto(pieces_each - 1) do |count| + piece = Piece.new(@board.positions[0], p, count) + @pieces[p][count] = piece + @board.positions[0].contains << piece + end + @players_cards[p] = [] + deal_cards!($INITIAL_CARDS_PER_PLAYER, p) + end + end + + def deal_cards!(number_of_cards, player = @current_player) + 1.upto(number_of_cards) do + @deck = @cards.shuffle if @deck.empty? + @players_cards[player] << @deck.pop + end + end + + # Check that a move is valid. Throw an exception if it's invalid + def validate_move(move, player = @current_player) + # Check the move is a valid one + raise(InvalidMoveError, "Move #{move}: Player #{player} does not exist") unless @players.include?(player) + raise(InvalidMoveError, "Move #{move}: None of player #{player}'s pieces on position #{move.origin}") unless move.origin.contains.find {|pc| pc.player == player} + raise(InvalidMoveError, "Move #{move}: Origin and destination are the same") if move.origin == move.destination + + origin_position = @board.positions.index(move.origin) + destination_position = @board.positions.index(move.destination) + # Is this move an advance or a retreat? + if destination_position > origin_position + # Advancing a piece + unless @players_cards[player].find {|c| c == move.destination.symbol} + raise(InvalidMoveError, "Player #{player} does not have a card to move a piece to a #{move.destination.symbol} square") + end + # Check target square is vacant + raise(InvalidMoveError, "Advance move #{move}: destination occupied") unless move.destination.contains.empty? + # Check all the intervening squares with this symbol are occupied + intervening_empty_position = @board.positions[origin_position...destination_position].index_find do |p| + p.symbol == move.destination.symbol and + p.contains.empty? + end + raise(InvalidMoveError, "Advance move #{move}: location #{intervening_empty_position} is empty") if intervening_empty_position + else + # Retreating a piece + # Check target position has one or two pieces already on it + destination_count = move.destination.contains.length + raise(InvalidMoveError, "Retreat move #{move}: destination has no pieces already on it") if destination_count == 0 + raise(InvalidMoveError, "Retreat move #{move}: destination has too many (#{destination_count}) pieces already on it") if destination_count >= $MAX_PIECES_PER_POSITION + # Check none of the intervening squares have any pieces on them +# puts "Checking positions #{destination_position} to #{origin_position}" + intervening_target_position = @board.positions[(destination_position + 1)...origin_position].index_find do |p| +# puts "Examining postition #{p} at location #{@board.positions.index(p)} which contains #{p.contains.length} pieces" + p.contains.length > 0 and + p.contains.length < $MAX_PIECES_PER_POSITION + end + raise(InvalidMoveError, "Retreat move #{move.show(@board)}: location #{intervening_target_position} is a viable target") if intervening_target_position + end + end + + # Apply a single move to a game. + def apply_move!(move, player = @current_player) + + validate_move(move, player) + + # Apply this move + move.origin.contains.delete move.piece + move.destination.contains << move.piece + move.piece.position = move.destination + + # Update cards + if @board.positions.index(move.destination) > @board.positions.index(move.origin) + # Advance move + @players_cards[player].delete_at(@players_cards[player].index(move.destination.symbol)) + else + # Retreat move + deal_cards!(move.destination.contains.length - 1, player) + end + + # Record the new stae +# this_game_state = GameState.new(move, player, @pieces) +# @history << this_game_state + + # If this player has all their pieces in the boat, declare a win. + if @pieces[player].all? {|pc| pc.position == :boat} + raise(GameWonNotice, "Game won by #{player}") + end + end + + # Undo a move + def undo_move! + if @history.length > 1 + # general case + state_to_restore = @history[-2] + @current_player = @history[-1].player + @pieces.each do |name, piece| + copy_piece = state_to_restore.pieces_after_move[name] + piece.position = copy_piece.position + end + @history.pop + elsif @history.length == 1 + # reset to start + @current_player = @players[1] + @pieces.each do |name, piece| + piece.position = @board.positions[piece.colour] + end + @history.pop + end + end + + # Apply a list of moves in order + def apply_moves!(moves) + moves.each do |move| + if move.via_base? + moved_distance = board.distance_between[move.piece.position.place][@current_player] + + board.distance_between[@current_player][move.destination.place] + else + moved_distance = board.distance_between[move.piece.position.place][move.destination.place] + end + self.apply_move!(move, @current_player) + next_player! unless moved_distance == 6 + end + end + + + # Set the current player to be the next player + def next_player! + original_player = @current_player + if @current_player == @players[-1] + @current_player = @players[1] + else + @current_player = @players[@players.index(@current_player) + 1] + end + @current_player + end + + + # Return an array of all possible moves from this state, given the active player + def possible_moves(player = @current_player) + moves = [] + @pieces[player].each do |piece| + # Do a forward move for each card held + unless piece.position == @board.positions[-1] + @players_cards[player].each do |card| + destination = @board.positions[@board.positions.index(piece.position)..-1].find do |pos| + (pos.symbol == card and pos.contains == []) or + pos.symbol == :boat + end +<<<<<<< .mine +puts "Player #{player}, card #{card}, piece #{piece.number} at position #{@board.positions.index(piece.position)} moving to #{@board.positions.index(destination)}, a #{destination.symbol}" +======= +# puts "Player #{player}, card #{card}, piece #{piece.number} at position #{@board.positions.index(piece.position)} to #{@board.positions.index(destination)}, a #{destination.symbol}" +>>>>>>> .r34 + moves << Move.new(piece, piece.position, destination) +puts "Added move #{piece.number}: #{piece.position.symbol} -> #{destination.symbol}" + end + end + # Do a reverse move for the piece + unless piece.position == board.positions[0] + destination = @board.positions[0...@board.positions.index(piece.position)].reverse.find do |pos| + pos.contains.length == 1 or pos.contains.length == 2 + end + if destination +# puts "Player #{player}, piece #{piece.number} at position #{@board.positions.index(piece.position)} retreats to #{@board.positions.index(destination)}, a #{destination.symbol} containing #{destination.contains.length} pieces" + moves << Move.new(piece, piece.position, destination) + end + end + end +# moves.each {|m| puts m.show(@board)} + moves + end + + def build_state_string + outstr = "Current player = #{@current_player}\n" + 0.upto((@board.positions.length)-1) do |i| + outstr << "#{i}: #{@board.positions[i].symbol}: " + @board.positions[i].contains.each do |piece| + outstr << "P#{piece.player}:#{piece.number} " + end + outstr << "\n" + end + 0.upto((@players.length)-1) do |i| + outstr << "Player #{i} holds " << (@players_cards[i].sort_by {|c| c.to_s}).join(', ') << "\n" + end + outstr << "Deck holds " << @deck.join(', ') << "\n" + outstr + end + + # Show the state of the board + def show_state + puts build_state_string + # @pieces.keys.sort.each do |piece_name| + # if @pieces[piece_name].captured + # puts "Piece #{piece_name} captured, at #{@pieces[piece_name].position}" + # else + # puts "Piece #{piece_name} is at #{@pieces[piece_name].position}, holds #{(@pieces[piece_name].contains.collect{|c| c.name}).join(' ')}" + # end + # end + end + + def to_s + show_state + end + + def to_str + to_s + end + + + def set_testing_game! + srand 1234 + @board = nil + initialize(6, 6, 6) + end + + # Given a set of lines from an input file, turn them into a Game object and + # a set of Move objects. + # Note the multiple return values. + # Class method + def Game.read_game(gamelines) + gamelines.each {|l| l.chomp!} + game = Game.new(gamelines[0].to_i, 6, 6, 6, 6) + moves = [] + gamelines[1..-2].each {|m| moves << m.to_move(game)} + return game, moves, gamelines[-1].to_i + end + + +end + + +# Extension to String class to convert a move-description string into a Move object. +# This is the inverse of the Move#to_s method +class String + def to_move(game) + move_elements = self.downcase.split + piece_name = move_elements[0] + destination_name = move_elements[-1] + if destination_name.length > 2 and + destination_name[-2,2] == game.board.centre.place[-2,2] + destination_name = game.board.centre.place + end + raise(InvalidMoveError, "Invalid piece in move read") unless game.pieces.has_key?(piece_name) + raise(InvalidMoveError, "Invalid destination in move read") unless game.board.positions.has_key?(destination_name) + # Deal with the synonyms for the centre position + via_base = (destination_name.length == 1 or move_elements.length > 2) + Move.new(game.pieces[piece_name], game.board.positions[destination_name], via_base) + end +end + + +# Read a game description file and convert it into a Game object and set of Move objects. +# Note that Game.read_game method returns multiple values, so this one does too. +class IO + def IO.read_game(filename) + gamelines = IO.readlines(filename) + return Game.read_game(gamelines) + end +end + +# Extension to the Array class to include the Array#shuffle function +class Array + def shuffle + sort_by { rand } + end + + def index_find(&block) + found = find(&block) + if found + index found + else + found + end + end +end diff --git a/lib/cgi/.svn/all-wcprops b/lib/cgi/.svn/all-wcprops new file mode 100644 index 0000000..217831e --- /dev/null +++ b/lib/cgi/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 45 +/svn/njae/!svn/ver/43/cartagena/trunk/lib/cgi +END +cartagena-simple.rb +K 25 +svn:wc:ra_dav:version-url +V 65 +/svn/njae/!svn/ver/66/cartagena/trunk/lib/cgi/cartagena-simple.rb +END diff --git a/lib/cgi/.svn/entries b/lib/cgi/.svn/entries new file mode 100644 index 0000000..4cf7e00 --- /dev/null +++ b/lib/cgi/.svn/entries @@ -0,0 +1,39 @@ +8 + +dir +65 +http://scripts.njae.me.uk/svn/njae/cartagena/trunk/lib/cgi +http://scripts.njae.me.uk/svn/njae + + + +2008-10-17T07:57:36.358504Z +43 + + + +svn:special svn:externals svn:needs-lock + + + + + + + + + + + +0ada9808-85ab-4924-adda-5bf89eae3818 + +cartagena-simple.rb +file +66 + + + +2009-01-30T14:54:15.000000Z +cabe60e563744fa37032ef3a9cad7b63 +2009-01-30T15:02:26.543445Z +66 + diff --git a/lib/cgi/.svn/format b/lib/cgi/.svn/format new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/lib/cgi/.svn/format @@ -0,0 +1 @@ +8 diff --git a/lib/cgi/.svn/text-base/cartagena-simple.rb.svn-base b/lib/cgi/.svn/text-base/cartagena-simple.rb.svn-base new file mode 100644 index 0000000..5b58ca5 --- /dev/null +++ b/lib/cgi/.svn/text-base/cartagena-simple.rb.svn-base @@ -0,0 +1,150 @@ +#!/usr/bin/ruby -w +# +# == Synopsis +# +# Play one move of a Cartagena game +# +# == Usage +# CGI script +# +# == Author +# Neil Smith + +require 'libcartagena' +require 'cgi' +require 'open3' +require 'timeout' + +# Prevent dangerous operations +$SAFE = 1 + +class InvalidRobotError < StandardError +end + +class NoInputFileError < StandardError +end + +class InvalidMoveInHistoryError < StandardError +end + +class GameWonInHistoryError < StandardError +end + + +# A table of the known game-playing robots + +$ROBOT_NAME_TABLE = { + 'First Possible' => '/var/www/scripts.njae.me.uk/cartagena/robots/1', + 'Random' => '/var/www/scripts.njae.me.uk/cartagena/robots/2' +} + +$ROBOT_CODE_TABLE = { + '1' => '/var/www/scripts.njae.me.uk/cartagena/robots/1', + '2' => '/var/www/scripts.njae.me.uk/cartagena/robots/2' +} + + +# How long the robot has to respond +$ROBOT_TIMEOUT = 15 + + +def generate_output(cgi, returned_move, annotation) + if returned_move.empty? + return_part = "" + else + return_part = cgi.p { "Result is: " } + "\n" + cgi.h1 { returned_move } + "\n" + end + + if annotation.empty? + annotation_part = "" + else + annotation_part = cgi.p { annotation } + end + + cgi.html { + cgi.head { "\n" + cgi.title{"Move result"} } + "\n" + + cgi.body { return_part + annotation_part } + } +end + + +begin + + cgi = CGI.new("html4") + + chosen_robot = cgi['robot'] + if $ROBOT_NAME_TABLE.has_key? chosen_robot + chosen_robot_dir = $ROBOT_NAME_TABLE[chosen_robot] + chosen_robot_dir.untaint + else + raise InvalidRobotError, "#{chosen_robot} is not a valid robot code" + end + + begin + game, moves = Game.read_game(cgi['history'].downcase.split("\n")) + rescue InvalidMoveError => error + raise(InvalidMoveInHistoryError, error.message) + rescue GameWonNotice => error + raise(GameWonInHistoryError, error.message) + end + + current_directory = Dir.pwd + current_directory.untaint + Dir.chdir chosen_robot_dir + + returned_move = "" + returned_error = "" + robot_input = game.players.length.to_s + "\n" + robot_input += game.board.positions.collect {|p| p.symbol.to_s}.join("\n") + "\n" + robot_input += (moves.collect {|m| m.format(game.board, true)}).join("\n") + "\n" if not moves.empty? + robot_input += (game.current_player + 1).to_s + "\n" + robot_input += game.players_cards[game.current_player].collect {|c| c.to_s}.join("\n") + "\n" if not game.players_cards[game.current_player].empty? + +# cgi.out { generate_output(cgi, "", "passing robot #{game.players.length.to_s + "#" + (moves.collect {|m| m.to_s}).join('!\n!') + "#" + next_roll.to_s}!") } +# cgi.out { generate_output(cgi, "", "passing robot #{robot_input}!") } + + + Timeout.timeout($ROBOT_TIMEOUT + 1) do + Open3.popen3('./runme') do |robot_in, robot_out, robot_err| + robot_in << robot_input + robot_in.close_write + returned_move = robot_out.gets + returned_error = robot_err.gets + end + end + + Dir.chdir current_directory + +# cgi.out { generate_output(cgi, "", "Returned move #{returned_move}; robot error was #{returned_error}!") } + + raise(InvalidMoveError, "Robot returned error '#{returned_error}'") if returned_error != nil + raise(InvalidMoveError, "Null move") if returned_move.nil? or returned_move.empty? + next_move = returned_move.chomp.to_move(game, true) + game.apply_move! next_move + +# cgi.out { generate_output(cgi, "", "Applied move #{returned_move}!") } + cgi.out { generate_output(cgi, returned_move, "") } + +rescue InvalidMoveInHistoryError => error + cgi.out { generate_output(cgi, "", "Invalid move in history: #{error}") } +# puts "Invalid move in history: #{error}" +rescue GameWonInHistoryError => error + cgi.out { generate_output(cgi, "", "Game already won in historic moves: #{error}") } +# puts "Game already won in historic moves: #{error}" + +rescue InvalidMoveError => error + cgi.out { generate_output(cgi, "", "Robot returned invalid move: #{error}") } +# puts "Robot returned invalid move: #{error}" +rescue GameWonNotice => error + cgi.out { generate_output(cgi, returned_move, "Game won: #{error}") } +# puts "Robot returned #{returned_move}" +# puts "Game won: #{error}" + +rescue InvalidRobotError => error + cgi.out { generate_output(cgi, "", "Invalid robot selection: #{error}") } +# puts "Invalid robot selection: #{error}" +rescue Timeout::Error => error + cgi.out { generate_output(cgi, "", "Robot timed out") } +# puts "Robot timeout: #{error}" + +end diff --git a/lib/cgi/cartagena-simple.rb b/lib/cgi/cartagena-simple.rb new file mode 100755 index 0000000..5b58ca5 --- /dev/null +++ b/lib/cgi/cartagena-simple.rb @@ -0,0 +1,150 @@ +#!/usr/bin/ruby -w +# +# == Synopsis +# +# Play one move of a Cartagena game +# +# == Usage +# CGI script +# +# == Author +# Neil Smith + +require 'libcartagena' +require 'cgi' +require 'open3' +require 'timeout' + +# Prevent dangerous operations +$SAFE = 1 + +class InvalidRobotError < StandardError +end + +class NoInputFileError < StandardError +end + +class InvalidMoveInHistoryError < StandardError +end + +class GameWonInHistoryError < StandardError +end + + +# A table of the known game-playing robots + +$ROBOT_NAME_TABLE = { + 'First Possible' => '/var/www/scripts.njae.me.uk/cartagena/robots/1', + 'Random' => '/var/www/scripts.njae.me.uk/cartagena/robots/2' +} + +$ROBOT_CODE_TABLE = { + '1' => '/var/www/scripts.njae.me.uk/cartagena/robots/1', + '2' => '/var/www/scripts.njae.me.uk/cartagena/robots/2' +} + + +# How long the robot has to respond +$ROBOT_TIMEOUT = 15 + + +def generate_output(cgi, returned_move, annotation) + if returned_move.empty? + return_part = "" + else + return_part = cgi.p { "Result is: " } + "\n" + cgi.h1 { returned_move } + "\n" + end + + if annotation.empty? + annotation_part = "" + else + annotation_part = cgi.p { annotation } + end + + cgi.html { + cgi.head { "\n" + cgi.title{"Move result"} } + "\n" + + cgi.body { return_part + annotation_part } + } +end + + +begin + + cgi = CGI.new("html4") + + chosen_robot = cgi['robot'] + if $ROBOT_NAME_TABLE.has_key? chosen_robot + chosen_robot_dir = $ROBOT_NAME_TABLE[chosen_robot] + chosen_robot_dir.untaint + else + raise InvalidRobotError, "#{chosen_robot} is not a valid robot code" + end + + begin + game, moves = Game.read_game(cgi['history'].downcase.split("\n")) + rescue InvalidMoveError => error + raise(InvalidMoveInHistoryError, error.message) + rescue GameWonNotice => error + raise(GameWonInHistoryError, error.message) + end + + current_directory = Dir.pwd + current_directory.untaint + Dir.chdir chosen_robot_dir + + returned_move = "" + returned_error = "" + robot_input = game.players.length.to_s + "\n" + robot_input += game.board.positions.collect {|p| p.symbol.to_s}.join("\n") + "\n" + robot_input += (moves.collect {|m| m.format(game.board, true)}).join("\n") + "\n" if not moves.empty? + robot_input += (game.current_player + 1).to_s + "\n" + robot_input += game.players_cards[game.current_player].collect {|c| c.to_s}.join("\n") + "\n" if not game.players_cards[game.current_player].empty? + +# cgi.out { generate_output(cgi, "", "passing robot #{game.players.length.to_s + "#" + (moves.collect {|m| m.to_s}).join('!\n!') + "#" + next_roll.to_s}!") } +# cgi.out { generate_output(cgi, "", "passing robot #{robot_input}!") } + + + Timeout.timeout($ROBOT_TIMEOUT + 1) do + Open3.popen3('./runme') do |robot_in, robot_out, robot_err| + robot_in << robot_input + robot_in.close_write + returned_move = robot_out.gets + returned_error = robot_err.gets + end + end + + Dir.chdir current_directory + +# cgi.out { generate_output(cgi, "", "Returned move #{returned_move}; robot error was #{returned_error}!") } + + raise(InvalidMoveError, "Robot returned error '#{returned_error}'") if returned_error != nil + raise(InvalidMoveError, "Null move") if returned_move.nil? or returned_move.empty? + next_move = returned_move.chomp.to_move(game, true) + game.apply_move! next_move + +# cgi.out { generate_output(cgi, "", "Applied move #{returned_move}!") } + cgi.out { generate_output(cgi, returned_move, "") } + +rescue InvalidMoveInHistoryError => error + cgi.out { generate_output(cgi, "", "Invalid move in history: #{error}") } +# puts "Invalid move in history: #{error}" +rescue GameWonInHistoryError => error + cgi.out { generate_output(cgi, "", "Game already won in historic moves: #{error}") } +# puts "Game already won in historic moves: #{error}" + +rescue InvalidMoveError => error + cgi.out { generate_output(cgi, "", "Robot returned invalid move: #{error}") } +# puts "Robot returned invalid move: #{error}" +rescue GameWonNotice => error + cgi.out { generate_output(cgi, returned_move, "Game won: #{error}") } +# puts "Robot returned #{returned_move}" +# puts "Game won: #{error}" + +rescue InvalidRobotError => error + cgi.out { generate_output(cgi, "", "Invalid robot selection: #{error}") } +# puts "Invalid robot selection: #{error}" +rescue Timeout::Error => error + cgi.out { generate_output(cgi, "", "Robot timed out") } +# puts "Robot timeout: #{error}" + +end diff --git a/lib/first-move.rb b/lib/first-move.rb new file mode 100755 index 0000000..4c96457 --- /dev/null +++ b/lib/first-move.rb @@ -0,0 +1,26 @@ +#!/usr/bin/ruby +# +# == Synopsis +# +# Play one move of a Cartagena game +# +# == Usage +# clockwise-p1 +# Game state file read on STDIN +# Move produced on STDOUT +# +# == Author +# Neil Smith + +# +# To change this template, choose Tools | Templates +# and open the template in the editor. + + +require 'libcartagena' +require 'rdoc/usage' + +gamelines = readlines +game, moves = Game.read_game(gamelines) +move = game.possible_moves[0] +puts move.format(game.board, true) diff --git a/lib/game_handler.rb b/lib/game_handler.rb new file mode 100644 index 0000000..cc8eda7 --- /dev/null +++ b/lib/game_handler.rb @@ -0,0 +1,59 @@ +# == Synopsis +# +# Play a game of Cartagena. +# +# == Author +# Neil Smith +# +# == Change history +# Version 1.1:: 23 April 2008 + +require 'lib/libcartagena' + +# Play a game to completion, given a set of player agents. +class GameHandler + + attr_reader :game + + # Create a game handler that uses a set of players + def initialize(players, game_length_limit = 5000, verbose = false, very_verbose = false) + @game = Game.new(players.length) + @verbose = verbose + @very_verbose = very_verbose + @game_length_limit = game_length_limit + + # Give each player a name + @named_players = Hash.new + (@game.players.zip players).each do |kv| + @named_players[kv[0]] = kv[1] + end + end + + # Play a game of Cartagena. If players make illegal moves, disqualify them and restart the game. + # Terminate the game if there's a winner, there's only one player left, + # or the game has gone on too long. + def play + while @game.history.length < @game_length_limit + move_to_apply = @named_players[@game.current_player].best_move(@game, roll) + puts "Move #{@game.history.length + 1}: Player #{@game.current_player} rolled #{roll}: making move #{move_to_apply}" if @verbose + @game.apply_moves! [move_to_apply] + puts @game if @very_verbose + end + puts "Game terminated after #{@game.history.length} moves" if @verbose + [:draw, @limit] + rescue GameWonNotice => win_notification + winner = win_notification.message[-1,1] + puts "Game won by #{winner} in #{@game.history.length} moves" if @verbose + [@named_players[winner], @game.history.length] + rescue InvalidMoveError + puts "Disqualifying player #{@game.current_player}" if @verbose + @named_players.delete @game.current_player + if @named_players.length > 1 + retry + else + puts "Game won by #{@named_players.keys[0]} by default" if @verbose + [@named_players[@named_players.keys[0]], 0] + end + end +end + \ No newline at end of file diff --git a/lib/html/.svn/all-wcprops b/lib/html/.svn/all-wcprops new file mode 100644 index 0000000..7aeda2e --- /dev/null +++ b/lib/html/.svn/all-wcprops @@ -0,0 +1,167 @@ +K 25 +svn:wc:ra_dav:version-url +V 46 +/svn/njae/!svn/ver/58/cartagena/trunk/lib/html +END +green_pirate.png +K 25 +svn:wc:ra_dav:version-url +V 63 +/svn/njae/!svn/ver/55/cartagena/trunk/lib/html/green_pirate.png +END +boat.gif +K 25 +svn:wc:ra_dav:version-url +V 55 +/svn/njae/!svn/ver/45/cartagena/trunk/lib/html/boat.gif +END +retreat.png +K 25 +svn:wc:ra_dav:version-url +V 58 +/svn/njae/!svn/ver/57/cartagena/trunk/lib/html/retreat.png +END +keys.jpg +K 25 +svn:wc:ra_dav:version-url +V 55 +/svn/njae/!svn/ver/45/cartagena/trunk/lib/html/keys.jpg +END +hat.jpg +K 25 +svn:wc:ra_dav:version-url +V 54 +/svn/njae/!svn/ver/45/cartagena/trunk/lib/html/hat.jpg +END +simple.html +K 25 +svn:wc:ra_dav:version-url +V 58 +/svn/njae/!svn/ver/42/cartagena/trunk/lib/html/simple.html +END +rules.html +K 25 +svn:wc:ra_dav:version-url +V 57 +/svn/njae/!svn/ver/46/cartagena/trunk/lib/html/rules.html +END +keys.png +K 25 +svn:wc:ra_dav:version-url +V 55 +/svn/njae/!svn/ver/45/cartagena/trunk/lib/html/keys.png +END +hat.png +K 25 +svn:wc:ra_dav:version-url +V 54 +/svn/njae/!svn/ver/45/cartagena/trunk/lib/html/hat.png +END +red_pirate.png +K 25 +svn:wc:ra_dav:version-url +V 61 +/svn/njae/!svn/ver/55/cartagena/trunk/lib/html/red_pirate.png +END +cell.png +K 25 +svn:wc:ra_dav:version-url +V 55 +/svn/njae/!svn/ver/57/cartagena/trunk/lib/html/cell.png +END +yellow_pirate.png +K 25 +svn:wc:ra_dav:version-url +V 64 +/svn/njae/!svn/ver/55/cartagena/trunk/lib/html/yellow_pirate.png +END +interface.html +K 25 +svn:wc:ra_dav:version-url +V 61 +/svn/njae/!svn/ver/46/cartagena/trunk/lib/html/interface.html +END +gun.jpg +K 25 +svn:wc:ra_dav:version-url +V 54 +/svn/njae/!svn/ver/45/cartagena/trunk/lib/html/gun.jpg +END +dagger.jpg +K 25 +svn:wc:ra_dav:version-url +V 57 +/svn/njae/!svn/ver/45/cartagena/trunk/lib/html/dagger.jpg +END +bottle.jpg +K 25 +svn:wc:ra_dav:version-url +V 57 +/svn/njae/!svn/ver/45/cartagena/trunk/lib/html/bottle.jpg +END +boat.png +K 25 +svn:wc:ra_dav:version-url +V 55 +/svn/njae/!svn/ver/57/cartagena/trunk/lib/html/boat.png +END +skull.jpg +K 25 +svn:wc:ra_dav:version-url +V 56 +/svn/njae/!svn/ver/45/cartagena/trunk/lib/html/skull.jpg +END +index.html +K 25 +svn:wc:ra_dav:version-url +V 57 +/svn/njae/!svn/ver/46/cartagena/trunk/lib/html/index.html +END +complex.html +K 25 +svn:wc:ra_dav:version-url +V 59 +/svn/njae/!svn/ver/58/cartagena/trunk/lib/html/complex.html +END +gun.png +K 25 +svn:wc:ra_dav:version-url +V 54 +/svn/njae/!svn/ver/45/cartagena/trunk/lib/html/gun.png +END +dagger.png +K 25 +svn:wc:ra_dav:version-url +V 57 +/svn/njae/!svn/ver/45/cartagena/trunk/lib/html/dagger.png +END +bottle.png +K 25 +svn:wc:ra_dav:version-url +V 57 +/svn/njae/!svn/ver/45/cartagena/trunk/lib/html/bottle.png +END +blue-pirate.gif +K 25 +svn:wc:ra_dav:version-url +V 62 +/svn/njae/!svn/ver/45/cartagena/trunk/lib/html/blue-pirate.gif +END +skull.png +K 25 +svn:wc:ra_dav:version-url +V 56 +/svn/njae/!svn/ver/45/cartagena/trunk/lib/html/skull.png +END +blue_pirate.png +K 25 +svn:wc:ra_dav:version-url +V 62 +/svn/njae/!svn/ver/45/cartagena/trunk/lib/html/blue_pirate.png +END +brown_pirate.png +K 25 +svn:wc:ra_dav:version-url +V 63 +/svn/njae/!svn/ver/55/cartagena/trunk/lib/html/brown_pirate.png +END diff --git a/lib/html/.svn/entries b/lib/html/.svn/entries new file mode 100644 index 0000000..9f5bcaf --- /dev/null +++ b/lib/html/.svn/entries @@ -0,0 +1,372 @@ +8 + +dir +65 +http://scripts.njae.me.uk/svn/njae/cartagena/trunk/lib/html +http://scripts.njae.me.uk/svn/njae + + + +2008-12-08T21:03:51.254104Z +58 + + + +svn:special svn:externals svn:needs-lock + + + + + + + + + + + +0ada9808-85ab-4924-adda-5bf89eae3818 + +green_pirate.png +file + + + + +2008-12-21T01:02:45.000000Z +6529f173ff4a2357ce8a2b35f59f4b4a +2008-12-02T15:51:28.975261Z +55 + +has-props + +retreat.png +file + + + + +2008-12-21T01:02:45.000000Z +6708b25489b8f455d1919997cbf060bb +2008-12-07T21:19:32.325161Z +57 + +has-props + +boat.gif +file + + + + +2008-10-22T08:21:37.000000Z +23eee87206d4c79d418003d928330790 +2008-10-21T15:24:11.718099Z +45 + +has-props + +keys.jpg +file + + + + +2008-10-22T08:21:37.000000Z +42c46cd908897ee683f2fd28188da438 +2008-10-21T15:24:11.718099Z +45 + +has-props + +hat.jpg +file + + + + +2008-10-22T08:21:37.000000Z +71474422d343530c758ca271ea06853e +2008-10-21T15:24:11.718099Z +45 + +has-props + +simple.html +file + + + + +2008-10-16T14:47:35.000000Z +5be43e2fd5c280555ba3e3d3f002657b +2008-10-16T20:38:07.769887Z +42 + +rules.html +file + + + + +2008-04-22T14:04:55.000000Z +93131d55d7ea7311cbe9f0b333b3c4ef +2008-10-24T21:30:40.873147Z +46 + +keys.png +file + + + + +2008-10-22T08:21:37.000000Z +914e3af1491cc9f1abdf05ee1eabe958 +2008-10-21T15:24:11.718099Z +45 + +has-props + +hat.png +file + + + + +2008-10-22T08:21:37.000000Z +10243670e82d10cd014a94289692c4e7 +2008-10-21T15:24:11.718099Z +45 + +has-props + +red_pirate.png +file + + + + +2008-12-21T01:02:45.000000Z +9815ffc52aeefeecbf2a74a3b7948459 +2008-12-02T15:51:28.975261Z +55 + +has-props + +yellow_pirate.png +file + + + + +2008-12-21T01:02:45.000000Z +841e2f51e26f556f83adcb0ed17a3b4b +2008-12-02T15:51:28.975261Z +55 + +has-props + +cell.png +file + + + + +2008-12-21T01:02:45.000000Z +eed7e5ed5823fb2ff0c49559583590a1 +2008-12-07T21:19:32.325161Z +57 + +has-props + +interface.html +file + + + + +2008-10-16T14:18:37.000000Z +5838426b483f70f5b3a1348e3519256a +2008-10-24T21:30:40.873147Z +46 + +bottle.jpg +file + + + + +2008-10-22T08:21:37.000000Z +716d077b7f69bd08d0c8c82d62db0e23 +2008-10-21T15:24:11.718099Z +45 + +has-props + +boat.png +file + + + + +2008-12-21T01:02:45.000000Z +7d6697bceadf3cb9fd0efb9dbe90327b +2008-12-07T21:19:32.325161Z +57 + +has-props + +dagger.jpg +file + + + + +2008-10-22T08:21:37.000000Z +6cb01e7fcf6d919da31a4e76582ed85b +2008-10-21T15:24:11.718099Z +45 + +has-props + +gun.jpg +file + + + + +2008-10-22T08:21:37.000000Z +dd8325ea3b0d6238778aa361036b78e8 +2008-10-21T15:24:11.718099Z +45 + +has-props + +robots +dir + +skull.jpg +file + + + + +2008-10-22T08:21:37.000000Z +876ae69155595d5382aa6d0f57c5bfa1 +2008-10-21T15:24:11.718099Z +45 + +has-props + +complex.html +file + + + + +2008-12-21T01:02:45.000000Z +2f3b2bae81894f64a9f9c6d68521395c +2008-12-08T21:03:51.254104Z +58 + +index.html +file + + + + +2008-10-24T21:28:42.000000Z +3aad4d96ddd9c1becc11cd26c55ad574 +2008-10-24T21:30:40.873147Z +46 + +gun.png +file + + + + +2008-10-22T08:21:37.000000Z +ad9520ba128ac8411bfd34eb2d4ec1f4 +2008-10-21T15:24:11.718099Z +45 + +has-props + +dagger.png +file + + + + +2008-10-22T08:21:37.000000Z +25166cd34066416d5570ec025e763d1b +2008-10-21T15:24:11.718099Z +45 + +has-props + +bottle.png +file + + + + +2008-10-22T08:21:37.000000Z +697d020086074d9db3113eb2e63e8342 +2008-10-21T15:24:11.718099Z +45 + +has-props + +blue-pirate.gif +file + + + + +2008-10-22T08:21:37.000000Z +b31c4824b42a831b962e0f0f7fad3c91 +2008-10-21T15:24:11.718099Z +45 + +has-props + +skull.png +file + + + + +2008-10-22T08:21:37.000000Z +20808c2e7e5a90962db0461c5f57d4b9 +2008-10-21T15:24:11.718099Z +45 + +has-props + +blue_pirate.png +file + + + + +2008-10-22T08:21:37.000000Z +6e8360a580cf0fe525eed6e30d19b557 +2008-10-21T15:24:11.718099Z +45 + +has-props + +brown_pirate.png +file + + + + +2008-12-21T01:02:45.000000Z +09e6579d62cc02c8688090583343a2b6 +2008-12-02T15:51:28.975261Z +55 + +has-props + diff --git a/lib/html/.svn/format b/lib/html/.svn/format new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/lib/html/.svn/format @@ -0,0 +1 @@ +8 diff --git a/lib/html/.svn/prop-base/blue-pirate.gif.svn-base b/lib/html/.svn/prop-base/blue-pirate.gif.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/blue-pirate.gif.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/prop-base/blue_pirate.png.svn-base b/lib/html/.svn/prop-base/blue_pirate.png.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/blue_pirate.png.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/prop-base/boat.gif.svn-base b/lib/html/.svn/prop-base/boat.gif.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/boat.gif.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/prop-base/boat.png.svn-base b/lib/html/.svn/prop-base/boat.png.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/boat.png.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/prop-base/bottle.jpg.svn-base b/lib/html/.svn/prop-base/bottle.jpg.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/bottle.jpg.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/prop-base/bottle.png.svn-base b/lib/html/.svn/prop-base/bottle.png.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/bottle.png.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/prop-base/brown_pirate.png.svn-base b/lib/html/.svn/prop-base/brown_pirate.png.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/brown_pirate.png.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/prop-base/cell.png.svn-base b/lib/html/.svn/prop-base/cell.png.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/cell.png.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/prop-base/dagger.jpg.svn-base b/lib/html/.svn/prop-base/dagger.jpg.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/dagger.jpg.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/prop-base/dagger.png.svn-base b/lib/html/.svn/prop-base/dagger.png.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/dagger.png.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/prop-base/green_pirate.png.svn-base b/lib/html/.svn/prop-base/green_pirate.png.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/green_pirate.png.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/prop-base/gun.jpg.svn-base b/lib/html/.svn/prop-base/gun.jpg.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/gun.jpg.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/prop-base/gun.png.svn-base b/lib/html/.svn/prop-base/gun.png.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/gun.png.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/prop-base/hat.jpg.svn-base b/lib/html/.svn/prop-base/hat.jpg.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/hat.jpg.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/prop-base/hat.png.svn-base b/lib/html/.svn/prop-base/hat.png.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/hat.png.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/prop-base/keys.jpg.svn-base b/lib/html/.svn/prop-base/keys.jpg.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/keys.jpg.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/prop-base/keys.png.svn-base b/lib/html/.svn/prop-base/keys.png.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/keys.png.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/prop-base/red_pirate.png.svn-base b/lib/html/.svn/prop-base/red_pirate.png.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/red_pirate.png.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/prop-base/retreat.png.svn-base b/lib/html/.svn/prop-base/retreat.png.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/retreat.png.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/prop-base/skull.jpg.svn-base b/lib/html/.svn/prop-base/skull.jpg.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/skull.jpg.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/prop-base/skull.png.svn-base b/lib/html/.svn/prop-base/skull.png.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/skull.png.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/prop-base/yellow_pirate.png.svn-base b/lib/html/.svn/prop-base/yellow_pirate.png.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/lib/html/.svn/prop-base/yellow_pirate.png.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/lib/html/.svn/text-base/blue-pirate.gif.svn-base b/lib/html/.svn/text-base/blue-pirate.gif.svn-base new file mode 100644 index 0000000000000000000000000000000000000000..8630faba0d06fc4d8272c7cb22c4e18e3e4d7f82 GIT binary patch literal 982 zcmZ?wbhEHb6k`x$xcZ-g%{HIgzgMj6tYr2U)w&BxWe3%2PpefO)2chES9jE`>6q=r zd)7TyOl$Wm`Im<*dKEJ7QTqCisjFVuruW8AJz6mDM10etx}_&O*PQO#a9*_A2sE_y3o}dDbO`ac-J()L7Bz>M-56Ku9$MFN@F5GIeq~U@3xnjg9)5i#OHL*R>zT$3 zX~qo3#=!9W|DWOi8HWEe8UCj+{5NL!&%kizKf{?b3}8HTi(3~6Z~lhPO%jQ=wjpJ6bb$zYrYG8k+$&@7;4plYBr zDDDObK+=y6BK@#)Y-Wh$knyl}prVFtC+QKQOwV=`G z;ITDf6IR}uZn#)!%OXLQs}rS;YHhhBkbGi7=jyXDcPbvob~UeMxMedl!GXaV0Gxhi AK>z>% literal 0 HcmV?d00001 diff --git a/lib/html/.svn/text-base/blue_pirate.png.svn-base b/lib/html/.svn/text-base/blue_pirate.png.svn-base new file mode 100644 index 0000000000000000000000000000000000000000..162585644c1b09fdc75b9f90d5d1e2b4ee282064 GIT binary patch literal 675 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H3?x5i&EW)62LgOTT-j{%x&3>^%Faq=Z&9tg zpj38Ht@gB9)iJHQlX`VW&6$f8#v^B$$I|CqY!m2G-&{M4fb z^G?J!Evj33vUAPpz76Lko9~Lp&(mnRY*4<@cjg_ho+IAvyK{GZw@hfP*!!dI(BJms z|ALD9vU*of+kCot=IRAI&uPzmJ8{iHvn3zHw*F{ZygR>ry42)HO3iyzyH8ACwybOB zq}j{nY}mGR#rj#RH_qF%b;glno3?D5wteTU0|yT5-@hQg?{po|FZLxte!>4K0t|g> zHR3>{7?Zr+UF_|W4N8F=&H|6fVj%4S#%?FG?LdNM1&--J8XTewcaFY{0P2$Qba4!k zxEy;ix4KC|q~W3SnJm|iXRAE*vviB1b}r33tNQ~@6<%+9&a(To@0 z%qqOSWY64=lV)9BH&Y@neEb-Dn{(FR89K=VyTiN|cb`-3D7jW;>!cPst;A;by)$R> ztr~-#@*FuWFXti}B2xKZ!NQqQ<3iZ2+@p>Td>MvPymD^be1b;~Y`psV>#kW*5i^vz z41;#wo_l|#@Z92--z_}REnf_~7CflfkaA{Ua*n5gkbo;~_)czRb)^U;@Ic3MqKt7kQOVzW~)X7@#4pfPy5`njxg HN@xNAdc#!s literal 0 HcmV?d00001 diff --git a/lib/html/.svn/text-base/boat.gif.svn-base b/lib/html/.svn/text-base/boat.gif.svn-base new file mode 100644 index 0000000000000000000000000000000000000000..849efa04870113692b678f9680167d9ec4defa9d GIT binary patch literal 4347 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 literal 0 HcmV?d00001 diff --git a/lib/html/.svn/text-base/boat.png.svn-base b/lib/html/.svn/text-base/boat.png.svn-base new file mode 100644 index 0000000000000000000000000000000000000000..a471eb12a3399171ffa91ba969f5813a0c16ebf8 GIT binary patch literal 1627 zcmeAS@N?(olHy`uVBq!ia0vp^DnM+_!3<)>-FO0|7>k44ofy`glX=O&z|0Wf6XFUK z1Oj6qG6o{!G-HM|Aejb4X)}$DXQlyB+RT|V&!nZDnF&NQ|DQPn(K1SohQKfgf!36r zZa{A^mIV0)GdMiEkp|)EamTaeQrP?(D}396B8i|}ND!sER&l;+LwRlQfTN8D}6nN$7(a;j(lzVXX?CZ*UPa(Cum8*bHCv(|21 z*87z)=;@i~l}TGS7oXP6c7FFO=B!Sx#>)8<+2xJv)~K&qE}OQuy!5+~UhNw9KYi;y zOS3F!Vee20{VKFHb=twE-8trG*yQ%U!k9`@JCH!H0j|N4b|A`?%Z4Mns3zH}pYsRih2N?u!hd_x7p6krppZu zX6>5(My9H7lVU9IUxUuD0SyC(Zpc=Q4xRBeSq~ zp>m7$Zd!kuz@cToJfC$wm$=Nf$N574lU??wY~D~(z#_Q3%k697j)3CI^S&2O7%Kcc zZrJ+niQzjo$K-Qyw%a*6Hods5^v8RVhTXCQ8I3d5|5jCO+FB`a<;H!(d` z!7(Q}BQ>{Dq0CUv(2(o@ZH5*G4mLJ+HdYRHc6Lrq4lW)MULJ049!Vi#ei2zIc{y1r z85so?J#_^o9c39A4RcK$149!N6M1z@TMHu_J!2CiMovyn9&R29US0_!MHxjSlEME2 z41ydCmJBM)jEW3Qf{e_9jQ@`?E@EI{WMu>csP7mUnV4Bv+1NQaxwsh_m?3gN5+aQx zDZs$Q$jr>d!pzFb!U8m=7AVKSBFHMFXz0i$9GJ+iR48K9IB_9|veU+cqCpows2C>| zHF0u@iAzXIsj8`KXlj|5nweWzS~We&gn?hmRgVdHU@6i$mSee*O{!I+=-ug_(sNoj3o{`ycM__pIQVcG7gsx&I8WJ5`JxZ)(Y< z$6P)>ejV^iMU1&NwZZ4&qj}rS)=yY39}}=@nV4LHQ*oEwxp`lgo_uO^`qA>%_4!+M zwWBUR`kbFwFzHx4!;0KfcfK4v|2({9fhLc^ktYq=UAwP4hZI-6?_PY##x;G3?vlwf zuNg(k=-*FTz}V-{@KrW@-Q_#oR~_f>ZkK#6I(5k!DcK7zG}gQ3IG+D8_ldwEk(!++ zZI2dRzH>a6`E03@yNvtuiL+bfpKq^vxO&a0Xt`R}Q(5!Ox9mx{=xY1Qi0wdvs9ntS zWo67?v%X5(%-(T6OeIC|)|_8F@7Nu^V^=5f)gL*Yv0Xu^s6zB{yg=TbvtFl;Yi(4q z&(wI*@FsFf`U~;?wME`PdX{cHdFF`2Td7JN@q&r!7D1Nh?dE3RHQ8R-I_1(cOUdMP ziFXbv$*(f~7wGzZD3MsYZtF&M%SRJDwjKDVe73^;$Vyc^&C5!>nt}(;YW5Z%w!Fpv zH0C_WSot=SD_2detJ4CXJY6<}apG2ghQEG{-_BO6%{8A{E3|9U!8YULqI2%8WbymF zRrF4$<$TdOwKFD_eU3AJbeBDFdhCw9(UI@gKIKrZE{IqWdhF8TiRb)kUr+M8Dj`+J zeCeI8>dxKM?s+JeMl2H23`^kDajAu`XErtJ5vjH%5aGc%7aIe7d zXz@MMA2qZ7GqgWxyOy-Q;N2t!H|M!aa_o-xRZC5EJNI~Bd-%f5?hpT(s`oyanY)-@ zWQn+w2owL__|M7ze*G)GnC5OIB>&a>%Sw&pXjYN7^w|Fl#}EGEYT6zxVSE1nO#pCa BBU%6e literal 0 HcmV?d00001 diff --git a/lib/html/.svn/text-base/bottle.png.svn-base b/lib/html/.svn/text-base/bottle.png.svn-base new file mode 100644 index 0000000000000000000000000000000000000000..106101576ae3de4211c50a26363e7e99aa7b43f2 GIT binary patch literal 4335 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 literal 0 HcmV?d00001 diff --git a/lib/html/.svn/text-base/brown_pirate.png.svn-base b/lib/html/.svn/text-base/brown_pirate.png.svn-base new file mode 100644 index 0000000000000000000000000000000000000000..2db25e98a9f8bd9f083e74df19479ee52038a018 GIT binary patch literal 612 zcmV-q0-ODbP)xUFmW4?VkEfFE00001bW%=J06^y0W&i*H z0b)x>L;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_000McNliru*b5vAIVy(^EN%b* z0Ub$1K~xyiUC~inf-n>Y;IlDvibSFqItH4gq>Th6A+-Jff7KX5*1L;s-`TT^a{wB9 zKeUvOE!^4nQLjH{g~R?Geg$vEe$0*cQRx&AH|Yor_Se2TJ=8(>YWa? zQ?nh$GFGuA+4~rJ@^uP+Cr;OL^6mZ9y-qKmop^NfKU=@+O|E>)WM_# zBM7mICzJV&s^~bvbjta%!}*kA1AIbf8zIsgAvT<11__~De8xm zPDue!&}UIzuHTJomCi|4w<;Sg)2fXx<#OcdmM1GWNRR5hTGeswnnly_t>*^oqqYv* yr>Gszi)E^U5Id04~t#g+()a`1F<46!(!oS-0P zP{`r^VWabnHGhPf-V`JX+C5vi)}zB9A>l!)B1=P;3TN{}xfzSQ+SVyCHaSf3a9!N! cY3j{B$b_ifK(iP;UHx3vIVCg!0G0q>zW@LL literal 0 HcmV?d00001 diff --git a/lib/html/.svn/text-base/complex.html.netbeans-base b/lib/html/.svn/text-base/complex.html.netbeans-base new file mode 100644 index 0000000..b8cc34e --- /dev/null +++ b/lib/html/.svn/text-base/complex.html.netbeans-base @@ -0,0 +1,314 @@ + + + +Cartagena player + + +

Cartagena player, Complex form

+
+

Game size: + + +

+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Cell + Cell + + Red + Red + Red + Red + Red + Red + Green + Green + Green + Green + Green + Green + Blue + Blue + Blue + Blue + Blue + Blue + Yellow + Yellow + Yellow + Yellow + Yellow + Yellow + Brown + Brown + Brown + Brown + Brown + Brown +
1 + Gun +  
2 + Keys +  
3 + Cell +  
4 + Cell +  
5 + Cell +  
6 + Cell +  
7 + Cell +  
8 + Cell +  
9 + Cell +  
10 + Cell +  
11 + Cell +  
12 + Cell +  
13 + Cell +  
14 + Cell +  
15 + Cell +  
16 + Cell +  
17 + Cell +  
18 + Cell +  
19 + Cell +  
20 + Cell +  
21 + Cell +  
22 + Cell +  
23 + Cell +  
24 + Cell +  
25 + Cell +  
26 + Cell +  
27 + Cell +  
28 + Cell +  
29 + Cell +  
30 + Cell +  
31 + Cell +  
32 + Cell +  
33 + Cell +  
34 + Cell +  
35 + Cell +  
36 + Cell +  
Boat + Boat +  
+
+ +
+ + +
+ + + + + + + + +
+
+
+ + + + + + + + +
    
    
    
    
Selected robot +
+
+ + \ No newline at end of file diff --git a/lib/html/.svn/text-base/complex.html.svn-base b/lib/html/.svn/text-base/complex.html.svn-base new file mode 100644 index 0000000..c8f41d5 --- /dev/null +++ b/lib/html/.svn/text-base/complex.html.svn-base @@ -0,0 +1,237 @@ + + + +Cartagena player + + +

Cartagena player, Complex form

+
+

Number of players: + + +

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Cell + Cell + + Red + Red + Red + Red + Red + Red + Green + Green + Green + Green + Green + Green + Blue + Blue + Blue + Blue + Blue + Blue + Yellow + Yellow + Yellow + Yellow + Yellow + Yellow + Brown + Brown + Brown + Brown + Brown + Brown +
1 + Gun + 2 + Keys + 3 + Dagger + 4 + Hat + 5 + Skull + 6 + Bottle +
  7 + Keys +
13 + Dagger + 12 + Bottle + 11 + Gun + 10 + Hat + 9 + Skull + 8 + Dagger +
14 + Bottle +  
15 + Keys + 16 + Gun + 17 + Hat + 18 + Skull + 19 + Dagger + 20 + Skull +
  21 + Bottle +
27 + Keys + 26 + Dagger + 25 + Hat + 24 + Hat + 23 + Keys + 22 + Gun +
28 + Bottle +  
29 + Gun + 30 + Skull + 31 + Keys + 32 + Bottle + 33 + Skull + 34 + Dagger +
  35 + Hat +
Boat + Boat + 36 + Gun +
+
+

Active player: +

Actions remaining: 3

+

Action to take

+ + + + + +
+ Gun
+ Hat
+ Skull
+ Retreat
+ +

Selected robot +

+

Players' card counts:

+ + + + + + +
3 (active)
3
3
3
3
+
+

Game history

+ +
+ +
+ + \ No newline at end of file diff --git a/lib/html/.svn/text-base/dagger.jpg.svn-base b/lib/html/.svn/text-base/dagger.jpg.svn-base new file mode 100644 index 0000000000000000000000000000000000000000..e8e1f372360aa88bb710ad0e637660657aced181 GIT binary patch literal 1396 zcmex=<;H!(d` z!7(Q}BQ>{Dq0CUv(2(o@ZH5*G4mLJ+HdYRHc6Lrq4lW)MULJ049!Vi#ei2zIc{y1r z85so?J#_^o9c39A4RcK$149!N6M1z@TMHu_J!2CiMovyn9&R29US0_!MHxjSlEME2 z41ydCmJBM)jEW3Qf{e_9jQ@`?E@EI{WMu>csP7mUnV4Bv+1NQaxwsh_m?3gN5+aQx zDZs$Q$jr>d!pzFb!U8m=7AVKSBFHMFXz0i$9GJ+iR48K9IB_9|veU+cqCpows2C>| zHF0u@iAzXIsj8`KXlj|5nweWzS~We&gn?hmRgVdHU@6i$mSee*O{!I+=-ug_(sNQ_-W z4u72Az5k(vSfeh>qk`klXP=UYsaf#g>AtMnAO5YIwQfG^lK@YqeF4XnnL<_`d{W@% z9@Xcqs(9ne4TFT9zS*vxU9Y^Ic=83ebT3V0^HjFEKL1tJ(`nP6=sxLLwEOO}; ze(Dm!!1#t;eyZ>#Lsz-R{IgpkUvCN7dTE=np^aDB%PjYR!+Q&)-bB^hs+oL0@_509 zW9KEN2safr{+JVP>Z>Kv$L{&z@71ecHfLV{bkK(Dlex*6( zyb^eMV%C(W>sVUa?n!QY-r(`7>S{>lk}g|2zTC&p?KM_vmu{Td{;2itlEy9b=aj71 z`&fEpL$oW?Mt$RV0qiV#hW{D*r~EOFxpZx%cTzpy%SZR!BmJHlKUl~9LF178k<|SU zLigtCrMgVXJ1FsJ7B4Lf%~ zY}@^xp(Wi<{M)8mv1_z89X8pQtj)(4t!{a&Uh3K%+w4V0?h4%c&#+Sd`CfnDmNG6) So~HP{KZ3PgH%`9u|0V!Z%uuiZ literal 0 HcmV?d00001 diff --git a/lib/html/.svn/text-base/dagger.png.svn-base b/lib/html/.svn/text-base/dagger.png.svn-base new file mode 100644 index 0000000000000000000000000000000000000000..0d4dabf5f23712ae7fab2ed77176351f10655ea2 GIT binary patch literal 4590 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 Y01wjH`oO)I6u4o3ERqb;Vz{ma13c8nv0K zqNkU#o~F5*u3+9ile>e1$Z)o%t8m;-zoE8W?JJMTe{$kfoxzc8@i&>viFE2zozIV> z%a_KozJlsztInW=@n?khSC02>miKv`{D7qXh#YVR-!GYR00001bW%=J06^y0W&i*H z0b)x>L;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_000McNliru*b5vAHY0gThy?%u z0Ub$1K~xyiUC~inf-n>Y;IlCuV2UYAL`GI9mf%nlLbUz=f7KXb*1L;s-`TT^a{yW^ zFR+x4HQZVCQLjH>gT7&eNf#y_>TSNXBSY5PH4P*?-ekyVKMEzw4mY10X*K^OsCU>k z4(+xZ$WXEu*mRufFUKU(fIt#zk4B@hrVchG z7(swlJRZ-lR7J-TrW4NN8_p*b8{iQ--3pOk39;oAGe`*KqAaUUDY%Uz07iLU)#pxK za!Lw-fU>VJrd8Q6&g(8dSMku*El-wynxE8rwd&K}w~MynYu8WrCvBhl yk5M<;H!(d` z!7(Q}BQ>{Dq0CUv(2(o@ZH5*G4mLJ+HdYRHc6Lrq4lW)MULJ049!Vi#ei2zIc{y1r z85so?J#_^o9c39A4RcK$149!N6M1z@TMHu_J!2CiMovyn9&R29US0_!MHxjSlEME2 z41ydCmJBM)jEW3Qf{e_9jQ@`?E@EI{WMu>csP7mUnV4Bv+1NQaxwsh_m?3gN5+aQx zDZs$Q$jr>d!pzFb!U8m=7AVKSBFHMFXz0i$9GJ+iR48K9IB_9|veU+cqCpows2C>| zHF0u@iAzXIsj8`KXlj|5nweWzS~We&gn?hmRgVdHU@6i$mSee*O{!I+=-ug_(sN)7VX}7OZSLq42NtaPTAZ zy_Y<^cfNhNt-8B&&kiNYvgC%v_crbkUH!^x@83JyIhqc{2<9C$GYIgwQCna*`Snqs zh)dt~s+}HZ9ABziwzkJF`{>d?GngKy72UZmTU(#MJZk3q4xs4U0Tyly-zx5Zhm1N`}4C^;q4|FTP$+d=uVl^^DbTc z`;EWP{=C@a?&^A4RQU4scl}FsOV{=#&-|D&FJM~&r@}nO_4D_{C+?4%bjo`6>lKd! zB4R8H*7m`gkF^&}(%J*C^@mwt5hPTd_344>_(kK8(Q-PY=W zvJ;=JHMKYt8?*{vIIiRR&mekkkKU&26SqIyo4nkWWmf9~-a-Sx-L6cBl$2NH#4)~{ z`<;lGddW}KgtIjzQUfnDQrkIBB$=Zn>6ycR1v#&CdP z-tv3vUPm3SsMAT)FxAMt7o)8l|Un$QBtCxMfAX zi1wgjyyGK@{YXr{|tvLlgj?=;VFzcw)sKgBikxz z?=6#bxz4hz5&UpHSmxt|B2fqVpsY}lxyQwmclQcOOfr2K=+xj>A@Zg_>fVvNg3 a?W1PRzN5S$&?7ao?&q?w?qs$4|2F}42u|q$ literal 0 HcmV?d00001 diff --git a/lib/html/.svn/text-base/gun.png.svn-base b/lib/html/.svn/text-base/gun.png.svn-base new file mode 100644 index 0000000000000000000000000000000000000000..b8c8237a0ed7a6dafebaa926ae59cfa31c2d8f2e GIT binary patch literal 4686 zcmV-U60z-xP)pQ0v8+h8rC6~Y z1u9q$=$IiM=}x>TVhcmMT$%AlH7f} zZ{OS3rrdV&VM&ox01pO(nZe-xaOU3oKmT*^ywlxwrxHd!7sFns6h>0=3(b`5dgpufCb2GF1RN_Z~#W2hsuvU000I6U@b9*i11qu0w@TS0i~QVhV?i4 z*1;x;1El?-v&K5pT5Co;kiZZe0$8JcPXGX18lCgYB(3db0D*Jh91sBja0J1@lN9)f zEoFVdd;|fYGUW#zp!wR@zh;p>@+S`p?iVT(gy<;nVz3q@2ZW0wU7|T_8Ru8Uwk9tZ$2yLBW~!eGCE)s6jYD4WI!HpaIaE zC*ux41E{y&AhXB}fEEZGKp=7eVRI!skD-&X1fu5-{p?F${c_~9cfac$h((&Ek_!fy zr$eVSB09#AG0r&vXswk}i0FA9B5JMQx;MrkqVGpsCLAuV8sSSKor1?{IDF;s%U}KX ze>-~kH=q3UL-ks`wD_h)D;W2=u!ZV(x>8E;JVFpcNGY9jS(a_`s?}$mv{n;IJbK9m5zx-mS+paWfDlZtpXsw?0 zQ^uG>X2?k?<2YuFrD-}C47An=W;h&btv$~ZLLg!kMPON@0YDcgQdnR-^k01D*Wdic z*VM4PZOi2ScfXUTJua{=&<*<~KVXavdR=g62@P{W5O7Yze%mOE)={o(mY-UEb!KLE zW4&vGNJfE_GOCvQ{h=~_DII5=02z`KT@BD7ErNi8A)`Dy zb@F6)W2089ZJ8JyoftRPDrY$pOmGs+X=}(Kpg?4dX{`+@AS(qUCp|6#FYvtJ(MKLx zX|BEd-g|DDm~1wiTj%G(IN+Q!ytF`o0HrrMHoj%c7M=C??Y-f^t$RCz4svFZkQ@Rq zA%sH!28_Zm%!|}%@;$HQMW8KN;Eeg4efOJBXacPZ>3KmCm*djrwp#HKK$TEcJJQ3w0vQHdQurw&ZP8$Q{7N({rdc9tn zrbM(E904%~+yMfUEDm@QbxqprueQ^6f6y+)USqW42aE*CYQ(Dk5cR;SQmIrfmCG{Z zg?aAKe;ua>G!(XurV>25A*THBw7$mzWCfrPdxqurKz(q7^KDC{d3ps-os^p z$b%rNlq$aPuS^Au0dPPL94us6p69s(6rQ}Wyi5pDxq{Lg>Oz4dDT(p%`qY`%7Itje zdG^%OSHAf5q*NbmOsuyy829hG`<}TSJF{G=!l}YJ2YFsx2@`lxlC3UOHcX3DS+qd1 zF*dGLzP8b}hQcr!8J{rDuC?1zu^V^Z^xdz2^Vy$26?(B!?!?IjLb&O+gSWin4j%g5 zYzSNwO6O_ed!;KgIkVbq-WX++GS*sW$QsjZHdj|yO<_u5OoYqJ&6NvHo$0CC__>p3 ze)Fs6TPtg0wNazpiPNjQuG@3(@4atwX6r!Z+SyV$_9S=C#9?q%WSk2i0JK7gC;DUU=<^?>sh9Z?IgSKX-2b{{0~F-uK=!yM4Bw z54wXcFtk>4j^GTx+M$R`i4CRn%W*kNl5}W{&CZ`b!#R^&CP`&>dMX_jhkpF?LqGh5 zudxwUF03?%z5ZBZq+BjdOi#@3oYziw`rSB*3Zv7kSBhdOQK=ljU505zLLxb9twltR z!dh!Gz4xYlt1HVY&n2)|4$S*}g45f6BLWcPAS4aa^(^mJ%Sk*s-dtKAZWvCowbpv_@h97B zt9h%-ayLBJdhcy_`qdbiJ=a@%;@KbF_u%`)s64m6QmZ#Qy|rF{z)<#k3KE}*z&Wti z3N8RZ0DyDA87gH|VTxQca-76hzWmk2H4g(Z z?N9#Y12^y6yV32A)<-OXb1shKEX|je&PGu*w{6#z&Xq9)&Jd7sCK%(431Xap-EhNA z)oRV-^8Dg?tL<20d}eYqNh+Qn%xs@qSUT5ix8L)-2k!g8`{Ouy%lvFw4=E)fFvh|- z5?oxB&tTKx2~T(d+bE>RpR8!pa0ia4!`U>Y*ZT@uvZkVH&6ZM*vXNJ zXx}Xd!!R7Ue06ozi~T4maV86sNzTT`8r4cAj-$)27Jz{RumIqkGuG)sWrIPlv$1ZB znwgmv42nFRnVi@%G2szeZPvOS>w7!)%x`3w7sV*$;?mOCY#&C|FFBk*s z^i^^N41f^;AvtSJVX`bA_J$jt&+=mU{l~s{?C{~4sfl_y$$Fh?xir7)n)QDF;eYtt zAOGn`pZv*>MCk9{y}R4)RI62VWULATFN}O^jWWvfJOXua1q^*P7=~z41yEdRu2Eo9k;)wNf7&zy9XE%WJI*%gyu6)l3zL7zBRk`AF6n z1K=0~B5Ry+4-0%0-)|$o`Yi+C5ver81F8M|#HnU8^ z;#f-<7FHBqz>54rbK!;Ui9NWz4%@`5f_mtS5YgZKg#y3?SJ*rhoWlZwd1dT=5Idp$xnZLbfmGc@TQP#WTft#BjXUc=Xp}_D2jalBIeT4 z2S7w1*en;A;0!q+thU;{{t%rjvRoG`&x*6>&n%o>dFGc-r1I|EjLdTdWRjQ>@Y0&A{l5!;d z&RX}(sgpxRpZ?UJ@7;G(T#k)Na~%xRL4TvmP*`iF)XrEbLQe>#)Z1W=z@l^7IAg6v z23#QLlapgi$lwEiV}7+a_}~K{+H>=PFb)nq^HdlIBV)C4DXv$OmGw0# zr4Ry-1EqQd(YLR;K6;x=F1nJ?<zupNG;Z!Q2LQl9Na=Zj;2t5j4!Yg$9e3PO ztJSpD^=idqXtk~<72gxzf9%oesfkXzwS0c5+wS;M%+73e#uiyV91aJAL7Jv%n)Zi- zZnsN#6%2?70Hu^dN-hO3v_Q)%tNU-cwOXy7J9o|+6GxFRML8+$z43-@IC%JPKfC9; z-CL%ouGzH%86nc>=xD80OOm8qF4ybzMx#-!)RUzAw#Z_}$^`z<+1w1Q9#GoT-Ja+9 zhu=86=@@1u`@AGqkI14pQ= z4a2BZDtEj6cB@k>*Q(`_mE~4*d6l7vqomvIr@8XOBrQ~bSTJBhh%2;_7=g7`8>5^v zjDZ(OZ3>5OxwZ1akNi<%dg6S0jg`W=Yj^M6cYqvsmX9{3N^wPNr!#}ZtEHON)b9@4 zYa2p_Nx4=iTdCExvDRAa>?M=RfCKX+_k{2S^8+46a(uF(U11oVU0(XLzj)|>kG^z# zY2o$7)5lJ}dHnQ3T&eE5=338>+v`28XnK5Rq&{v6=QM;~%u&Aj>g&Jw+0)IHRmPYn zJ*~B|hFz+z8RwjH20+$or87bomV+P(n;UH`dG|l?p+Yhfc+GC-;GK7V{8JC@x_(cm z+qaf{-&aLZ40DNmwAPp&pCIL)|J8H<`PhH_^oKvmvJ3zMKj54TqDy@m>#Wt4QE!BCDIccJ zU?Swy$jC?(pI=xwcJ%1&x8FrX3mU+JBGjrU6vI- zu=UnzWqMmJDH%mei%W-}f4C-Yd%o~}5e0z&X^^-Op(Ecb3^`8*zLZfEAsDR-24wt#z?Q?L z-C7^5j}5xn#MJDOqo zo@W4+QUpv&$vF>#z#64g&XJroj6pd}CdcP(Znw|Xm(E=H&bJ>A!rH@s_tyt*xqWim zeCP)N;0q}(+D2fEd9~_z7{*9e2(EN$j3GoJ1Tk){b;yJeo2@Wolu}ACN7f;OQWR%{ z)Oa?jHNO4#pFewc`4gXb=)(_wtTHwU$bc9CNEv`3GDL2(&L&Nyi4>6{mysD-J0!}9 zly$kG+>y%Nz)cD=AMbO4U*D%uCi&@XSq2PgQWtNzO>k ztyCy8)H5{X0svU24p}J3-Gl%D02+E!SaeEIMMo_|Z)|UJb8ul}WdHzp+A{(G05bqH z5f*-90000bdQ@0+N>D{dEmL7_aBOLAXDw!7V{~tFa{vH%+B2#!)G>r$01<;H!(d` z!7(Q}BQ>{Dq0CUv(2(o@ZH5*G4mLJ+HdYRHc6Lrq4lW)MULJ049!Vi#ei2zIc{y1r z85so?J#_^o9c39A4RcK$149!N6M1z@TMHu_J!2CiMovyn9&R29US0_!MHxjSlEME2 z41ydCmJBM)jEW3Qf{e_9jQ@`?E@EI{WMu>csP7mUnV4Bv+1NQaxwsh_m?3gN5+aQx zDZs$Q$jr>d!pzFb!U8m=7AVKSBFHMFXz0i$9GJ+iR48K9IB_9|veU+cqCpows2C>| zHF0u@iAzXIsj8`KXlj|5nweWzS~We&gn?hmRgVdHU@6i$mSee*O{!I+=-ug_(sN@w=Y)#a8ah%|rdVM~*U|@VlarKEdky`#7^2|3}%!(rUxsJPy^kEgK(HiAmZw zEOy>K*>BqAuq|_^J-YjenTI{7GRT(W!S73prX-e>BulRi3#f8ce(hcUS$9#vQfCDV zTX$J4pZQ*2rX4@I`_8kP6z-P`U8+AxbNB7nTtDm0o1>=>Og!}2&;8d{)|s;}%Y0d< zb?Q_4=BZv>#?P#^?V00ZR@p69y1lGOsn_PD@w+OnS6}4$Gow=9dv<;5w#tQps?~_prE^uRFhszw}%0n^S2u=Xp1y*ag8k`EPISwBLB!QD^dD7EPVz zXA_e{%YIEfxsL17mYYjdE@g@}O_OVt8UFqfINxreY?SF&*YJM>aJLt-+8S|fEoxNArinH(P|K9`v D)sS2Z literal 0 HcmV?d00001 diff --git a/lib/html/.svn/text-base/hat.png.svn-base b/lib/html/.svn/text-base/hat.png.svn-base new file mode 100644 index 0000000000000000000000000000000000000000..94a7ad56ee7db546666ec23d6dc653aa6fc39bd2 GIT binary patch literal 4561 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 literal 0 HcmV?d00001 diff --git a/lib/html/.svn/text-base/index.html.netbeans-base b/lib/html/.svn/text-base/index.html.netbeans-base new file mode 100644 index 0000000..99a592c --- /dev/null +++ b/lib/html/.svn/text-base/index.html.netbeans-base @@ -0,0 +1,54 @@ + + +Cartagena + + + +

Cartagena

+ +

Changes

+ + + + + + + + + + + + + + + + +
24 October 2008Added the libcartagena library.
25 September 2008Slightly changed the interface description: different file format, and program now has 15 seconds to report a single action, rather than 30 seconds for all three actions.
4 April 2008Initial version
+ +

Cartena is a simple race-based board game. You can read the +rules and look at the board to see how the game is played. More details on Cartagena are at Board Game Geek

+ +

The challenge is to build a computer program that will play the +game well. The structure of the challenge is heavily based on the one +for Pousse. What needs to be fixed +for the game is how your player will be called.

+ +

The Web interface

+ +

Here you can find some simple web forms that will allow you to +interact with the automatic Pousse players. There're two forms +here:

+ +
    +
  • The simple form, intended for your own +program to interact with; and
  • +
  • The complex form, intended for a human player.
  • +
+ +

The library

+ +

I've written a Ruby library libcartagena.rb, to act as a reference implementation. This library is released under the GNU General Public Licence v3.

+ + + diff --git a/lib/html/.svn/text-base/index.html.svn-base b/lib/html/.svn/text-base/index.html.svn-base new file mode 100644 index 0000000..99a592c --- /dev/null +++ b/lib/html/.svn/text-base/index.html.svn-base @@ -0,0 +1,54 @@ + + +Cartagena + + + +

Cartagena

+ +

Changes

+ + + + + + + + + + + + + + + + +
24 October 2008Added the libcartagena library.
25 September 2008Slightly changed the interface description: different file format, and program now has 15 seconds to report a single action, rather than 30 seconds for all three actions.
4 April 2008Initial version
+ +

Cartena is a simple race-based board game. You can read the +rules and look at the board to see how the game is played. More details on Cartagena are at Board Game Geek

+ +

The challenge is to build a computer program that will play the +game well. The structure of the challenge is heavily based on the one +for Pousse. What needs to be fixed +for the game is how your player will be called.

+ +

The Web interface

+ +

Here you can find some simple web forms that will allow you to +interact with the automatic Pousse players. There're two forms +here:

+ +
    +
  • The simple form, intended for your own +program to interact with; and
  • +
  • The complex form, intended for a human player.
  • +
+ +

The library

+ +

I've written a Ruby library libcartagena.rb, to act as a reference implementation. This library is released under the GNU General Public Licence v3.

+ + + diff --git a/lib/html/.svn/text-base/interface.html.svn-base b/lib/html/.svn/text-base/interface.html.svn-base new file mode 100644 index 0000000..d9d76ac --- /dev/null +++ b/lib/html/.svn/text-base/interface.html.svn-base @@ -0,0 +1,123 @@ + + +Cartagena Interface + + + +

Cartagena Computer Interface

+ +

Changes

+ + + + + + + + + + + + + + + + +
16 October 2008Another slight tweak. Introduced notation for the card played for an advance move.
25 September 2008Slightly changed the interface description: different file format, and program now has 15 seconds to report a single action, rather than 30 seconds for all three actions.
4 April 2008Initial version
+ +

The game will be played between two and six players.

+ +

Each player in Cartagena has up to three actions (advance or retreat moves) during their turn: your program will return just one of these actions.

+ +

Your program will be run afresh for each move it has to make. Your program will be fed the history of the game so far on standard input: this is likely to end with the previous actions taken during this turn. Your program will then have 15 seconds to report its next action on standard output.

+ +

The format of the input is as follows. The first line will have a single digit, giving the number of players. The next portion will state the layout of the board, from start to finish. Subsequent lines will give the moves played so far in the game (maximum of three consecutive actions by any player). Next will be a single line giving the number of the player whose turn is next. The final set of lines will be the cards in your hand (in an arbitrary order).

+ +

Postions are numbered consequetively from the start, with zero-based numbering. The cell is location 0. The boat is always the last position (number 37 in a standard game).

+ +

Players are numbered with one-based numbering: the first player to go is Player 1, the second is Player 2, and so on.

+ +

Each move will be in the format
+<player number> <location moved from> <location moved to> <card played>
+(with a single space between the fields). The <card played> field is always missing for retreat moves, is required for advance moves into the boat, and is optional for other advance moves.

+ +

It may be that a player takes fewer than their three allowed actions in their turn. There are no additional spaces or blank lines in the file.

+ +

Here is an example of a valid input file:

+ +
3
+cell
+skull
+hat
+keys
+gun
+bottle
+dagger
+hat
+gun
+keys
+skull
+bottle
+dagger
+dagger
+gun
+hat
+bottle
+skull
+keys
+hat
+keys
+bottle
+gun
+skull
+keys
+keys
+skull
+hat
+bottle
+gun
+dagger
+bottle
+skull
+gun
+dagger
+keys
+hat
+boat
+1 0 4
+1 0 3 keys
+1 4 3
+2 0 4
+2 4 3
+2 0 2
+3 0 5
+3 5 2
+3 0 4
+1 0 5 bottle
+2
+hat
+skull
+hat
+
+ +

(Note that in this example, player 1 elected to take only one of their three actions for their second turn. It is now player 2's turn.)

+ +

The output of your program will be the actions taken in your turn, in the same format as specified in the input file: one action per line, in the format
+<player number> <location moved from> <location moved to> <card played> (<card played> is optional for advance moves that are not into the boat).

+ +

Player programs will run on a Linux machine (probably some version of Ubuntu Linux). Exact specification is unclear at the moment, but it may run on a twin-processor machine.

+ +

The player's program must be named runme; it may be a binary executable or a driver shell script that starts up the actual program. It will be invoked as
+ +

    ./runme

+ +with the program's top-level directory as the current working directory; libraries and other support data may thus be accessed with the relative path name
+ +
    support/...

+ +

The program is run once for every move and then terminates; the current state will be passed to the program on standard input. Persistent inter-move state may be kept in the support/ directory if desired. When the program has chosen a move, it should print the move to standard output and exit. If the program has not done so after 30 seconds (real or "wall clock" time), the program will be terminated forcibly, and lose the game.

+ +

There should be no child processes alive after the top-level process exits; the program must ensure that all its child processes have exited before the top-level process exits itself.

+ + + diff --git a/lib/html/.svn/text-base/keys.jpg.svn-base b/lib/html/.svn/text-base/keys.jpg.svn-base new file mode 100644 index 0000000000000000000000000000000000000000..a0f655ea043bdd973170af4c07ee82b5a4871c98 GIT binary patch literal 1472 zcmex=<;H!(d` z!7(Q}BQ>{Dq0CUv(2(o@ZH5*G4mLJ+HdYRHc6Lrq4lW)MULJ049!Vi#ei2zIc{y1r z85so?J#_^o9c39A4RcK$149!N6M1z@TMHu_J!2CiMovyn9&R29US0_!MHxjSlEME2 z41ydCmJBM)jEW3Qf{e_9jQ@`?E@EI{WMu>csP7mUnV4Bv+1NQaxwsh_m?3gN5+aQx zDZs$Q$jr>d!pzFb!U8m=7AVKSBFHMFXz0i$9GJ+iR48K9IB_9|veU+cqCpows2C>| zHF0u@iAzXIsj8`KXlj|5nweWzS~We&gn?hmRgVdHU@6i$mSee*O{!I+=-ug_(sN!eZS0b&Ydvv4JEJ)BH1+EY)z{us^)|aKy{D4I z_Vji0%g9|TJr~Wfy>h&EzrV!aH+6G0AALGAPo-bwQGZtFQ?(4yz9RQali!^F`|Qu_ zyfSwsi}g}JYVO3`$tu|GU97w5pp1}!a_rjO1-AV^Lb=Tnb2=@K9AG$5JUOm3dR5QM zybm62pTjRdn&9@`^~T&q8WVP^UvBLAXS?d H1b8@=-tCNVvbF~6(a#PO#1#+Bm* z1(U*Uc|Ei^UUrpMX(vn1`OnbB)5PIaa`NOl;mE5>?f!eZG%wscE~qVJ|DMyf;p?%w znWD!-y$4zZO4^9=Sj^0nW^7In=j5@y?gn_H=d{XijP|DRo!@^P+riqy4T#VC-Tszm|Y$h z=kAmYoWmbx+rIHn&Gdu2>Nc*n+59>#q0jP~$C)l;;pWNP?|iX*JLhxPvlW+eZixl% zT)+O-ox87#W|yii`E-?~+KEkJn~Y3RK~YgfV(hZ%OK#r0=c?lM@al$1HcL;$JI-sK zQyBBg^XBZ3JWb_`4iA#1?G@d4tazSIn{{`@!l}$hp7U_5v-939E8(7P{4;v?+gMaw*e^!&oXCvhaaAAxGcc}O>_5ZwjmPWx zvzy$s=S|x7?CafIv3(y(M7F+K+ZR0dW5C~|J5?HYGax?jG+2-Ri)5QfnMK)~+^9;{UNuBeT+fwd# zw#O^&%ykDdl`Pzy_2)TgHSkQTpH^G=I$%0m{&9TNV8#msV_)|coT40h$ tV-L^Ow}-EU2f68aJt@2YO88~a1@44JjZ@SAGerIfDP45q=)3y=Hvt@5X+{75 literal 0 HcmV?d00001 diff --git a/lib/html/.svn/text-base/keys.png.svn-base b/lib/html/.svn/text-base/keys.png.svn-base new file mode 100644 index 0000000000000000000000000000000000000000..df6147f25ad2567a94fb56aacf5fdb39b9f20d54 GIT binary patch literal 4970 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 + +Cartagena rules + + + +

Cartagena rules

+ +

Changes

+ + + + + + + + + + + + +
22 April 2008Revised deck size: 17 cards of each type.
+Clarified that this is the Jamaica version
4 April 2008Initial version
+ +

Cartena is a simple race-based board game for 2-6 players. It is based on a famous jailbreak by a group of pirates in 1672. The board is a sequence of 36 positions. Each player controls a group of six 'pirate' pieces. Initially, all the pirates are in the jail at one end of the board. The first person to get all their pirates off the other end of the board (into the 'rescue sloop') is the winner.

+ +

These rules describe the Jamaica variant of the game.

+ +

The board

+

Each position on the board is marked with a symbol (one of bottle, dagger, key, hat, gun, and skull). The board is made from six segments, each of six positions. Each segment contains each symbol once, in an arbitrary order. Positions are numbered, 1-36, from the start to the end of the board. Each position can have zero, one, two, or three pirates on it at any time. Who owns the pirates is irrelevant: pirates owned by different players can be on the same space with no special effect. All pirates start the game before the first space (position zero). After the final space (position 37) is the goal (the rescue sloop).

+ +

The cards

+

There is a deck of 108 cards, consisting of seventeen each of the six symbols (bottle, dagger, key, hat, gun, and skull).

+ +

Setup

+

Each players pirates are placed on position zero. The deck of cards is shuffled and each player is dealt three cards. Players can see the contents of their hands, but not the contents of the other players' hands.

+ +

Taking a turn

+

Each players takes a turn in sequence. During a turn, each player may take up to three actions. The actions are either:

+ +
+
Advance a pirate
+
A player plays a card and moves one of their pirates forward on the board to the next empty space with the same symbol as the played card. The played card is placed on the discard pile.
+
Retreat a pirate
+
A player moves one of their pirates backward on the board to the next space with one or two pirates on it (spaces with zero or three pirates are skipped). The pirate cannot skip spaces with one or two pirates on it. The pirate cannot be moved back to the starting position. A pirate can be moved backard out of the rescue sloop. The player draws a number of cards equal to the number of pirates that were on the space before the move (i.e. one or two cards).
+
+ +

If the deck of cards is ever exhausted, the discard pile is shuffled and becomes the deck.

+ +

Back to the Cartagena page.

+ + + diff --git a/lib/html/.svn/text-base/simple.html.netbeans-base b/lib/html/.svn/text-base/simple.html.netbeans-base new file mode 100644 index 0000000..4142904 --- /dev/null +++ b/lib/html/.svn/text-base/simple.html.netbeans-base @@ -0,0 +1,76 @@ + + +Simple Cartagena form + + + +

Simple Cartagena form

+ +
+ + + + + + + + + + + + + + + +
Robot input:
Robot: +
+ +
+ + + diff --git a/lib/html/.svn/text-base/simple.html.svn-base b/lib/html/.svn/text-base/simple.html.svn-base new file mode 100644 index 0000000..4142904 --- /dev/null +++ b/lib/html/.svn/text-base/simple.html.svn-base @@ -0,0 +1,76 @@ + + +Simple Cartagena form + + + +

Simple Cartagena form

+ +
+ + + + + + + + + + + + + + + +
Robot input:
Robot: +
+ +
+ + + diff --git a/lib/html/.svn/text-base/skull.jpg.svn-base b/lib/html/.svn/text-base/skull.jpg.svn-base new file mode 100644 index 0000000000000000000000000000000000000000..0575e00f14fc333bb5dee79eea132514c35a229a GIT binary patch literal 1478 zcmex=<;H!(d` z!7(Q}BQ>{Dq0CUv(2(o@ZH5*G4mLJ+HdYRHc6Lrq4lW)MULJ049!Vi#ei2zIc{y1r z85so?J#_^o9c39A4RcK$149!N6M1z@TMHu_J!2CiMovyn9&R29US0_!MHxjSlEME2 z41ydCmJBM)jEW3Qf{e_9jQ@`?E@EI{WMu>csP7mUnV4Bv+1NQaxwsh_m?3gN5+aQx zDZs$Q$jr>d!pzFb!U8m=7AVKSBFHMFXz0i$9GJ+iR48K9IB_9|veU+cqCpows2C>| zHF0u@iAzXIsj8`KXlj|5nweWzS~We&gn?hmRgVdHU@6i$mSee*O{!I+=-ug_(sNnJGzvmo%em#fj$v%f~%hlEt@u`o~(hp2nXLNLb ztDD1t7akJptS;_Zd*{|9V?7)1;#IzjRz{i*6h&v_A16|F-F#mh{5Y z8a7V~_WtI2c+|`DrmV=?q^(!)lo{8j?Y%ZXHDR?1CzH^p2`8B3>|Wm8QGIz!tkt<^ z$Ni_hbUp3M;%?MX7qqxxuShl5jJ3kab7t8FiFiD8>2+GPf@?tn;|o^1y{sG06x}n~ z`CE7G?3|O8dp3BpGHdy(e6ULX_ZQ@taq`e$(I<)TIjK05$)9t}>rIp*;*GSv%wwZIqYtrQ1+%Igm zXbZW&+abGp@>LtfmHXChH@obrBzuZ~(oUE2+$WD2c$~Lgtr>9H*~3$L^JU%o&0kJu zyG?zPv6w5)?aq&Pa=lr#Xz4lr^RQDCj2okZ|3SN&)~v2myjWSOSm)l}ME&Ns=VgOFJ1) zYX@y4V`kA-tx~O)2m1zX(-Hv(kUUDLp<~1mLI6mCy8gc(8>s-)ABrIb7*QPqVTdA( zI9CKi%-cy=Uz)#KDJ@l3N+OZFckjt|^@Z)ouxt*9Xbcde zQh+322}miWltN)7qOj?@`rP=3M_)hWnryJQXJULj43eE=H480vk zrj}9wN~un4lv15|=$fAJ1OOOi2#^8=00@`>0&q>9DkWUk1_*q=yt=>p@WH1J&dl_7 zb$5679)IKT)co?AwS#U~zT9YlMq8~Qolb+iCIbLM2+p|>!t=Z&NdSOSs+8iKQ%X~& zYZy8BILs7b1t4EK^xWhB{hxQ=wiolbznH%Jj$0Sz7pLcz9Rr^`^Y-c+*85@T^C*g< zhMz?WI!OTlobx!2lO#!})0}gJ5T#T~$vID@Ql4AL7c2xC#u#HvHAV%GMut~+=W|v{ zzwb-;dMWMTZ=Wp|yYm@$&;DC4PR`USWj~1n$qA#i>IznX5W*NEgoF@L6h%>l6qiyG zLNrYiLP#k!ij)$Jf>NDiRpi@${|_pObNS4w-mc~4c}-`>-ag?6;YSk_sdPHq->nSN z2y42{>cKLCIHhR}R1&q4s0|P&Q7aCc6w4s=IgjEbKnM_4Q512dEXy`bn-L8oK!7MQ z^tc_@tCgvRg|p}1V^~aFdY@pN%jXBzt_|V{VUh^$FE86UdvTI}LMf#L;ZM#b z)(lhA4LhB2GFc;&CYD8PGeW>IT%Md%8kGoAmNnSd9|l2JzR=Ut!*mT(;$_mF=XG+V z6T*%WN+|+}VZsQ(o!aur8BdfFGNw2ZvU%ysIijLwt7bB~v{Yh5E98oEv-3QTip65B zzM>Gx_4LFE#~4vc1&`x64#O}A0zwD?PQnNj(RCeTYTIt1*gvpp1EWsIFiHRjripA5 z%ot;Svr%vQ4XI}5=ex4q7Zxt0EjMksQKOyKor%e*vnS8w3I!=e7>48x+A&(n-`7$q6Cf00Agxxd-3n5vl&$SQp-@;_o^7>jpp>rbRMFK#tInM~J2if#JDcCKVbh;I^ruu~rjc5ymg6{Hsja+l z_{Ev2so_4KmYm96^Pqz`^?UryL8=b zx01z$N-o=#_VOvysg&zM5cT!+ZQ8V{QmM>LUR_#Tn4h2Bv29CFv1@L6hGG&0p_CE< z!U%K0v5+cCBB4~wd8-*#%C%NAB!v9p7r)rE=f?Z*|4Lt9-{Rt8)aJcCLpiUzQH{&X ze%w}JGkN5vk38|~$1F2-@8|D6bNZB%GCkLxou0aI{v3}Jjp8^AgCMwe<0yq0RtBRK z5s(N3W5hI=!HDKqM%uN4woftGrkygq<44b&JaHjqX+pQ!Cq=r*mMt;rT zzIEGs=gy`~MifXcVEK6PL%gY}(x0(^bf% zpFjMGDiN$Ok|GK>Z&=?~ z?0)%$7hThO@V@)0rc=_fN@87O9iKX0E}t#frkgUXwCjmD*|~k20y%N@$}5Lo&ZP^t z?mw_))7bW{+fN)nTPe3}+q13ojvYI8?b^*4vs1=1PyJ?TVSeNKQQJ&Sj!)Pr$1?5N z>8VcJqLjh_QpyB`Qn9F3>k|``mo8nJo}TtR@7ZUc9qj8J8D8_^i!a`J=jY~Tm&z-R zwQJWWF^uiJaog6hAN}axO3USu;WaZ;SKm2#s!^*x^uYb5&is1y;lKQ=U1K}G`OR;B z_`wIh?*jnAgmTaomI$C$uk`eF|LtEtI)3TmXZG)V=h*q);D{@) z!ootc*>oKj0H^{<07X&T&=^CiUMa5|9^A8g`{5U#pPRXO+pW8VXx)C>-nUP@j)?-P z_U_-a_vSrRr+&Mso7(7xb@gU#VtVqXeV=~k-E(ig^+r#3-z~Qu$Yk;gwEdbjy6whH zp%(yxFd+ZoSgh<_l_-HUAg(W>1ugi)2XI0s;Sx8nU!+s(4j-ChDPtX_Y2)! zgXv6e?dZmRpT5=06{lyX7}dReR@11WNVg5$Fc@K-o|Sy}JKsYH0a9VqZnyo)%JRa( z)a|$5Vj3)$^&HQULiP^~AjB|b)k=d4lyxcjQaJa>$-@_rP87z*s=|U zP{IShS;vk}NT(s8AOYH~wqse$Oij+rZr`>0@T*6rrpJ4GiksGtuHCYI^8F7GF%&Yg z8K>2ZE?*oUpO}5|g(I7`>?*C)E?&9vwXc6oDV0P~5(Gi3r6>-Awx%08Bc`b{$^ZZp zOcUPLDYY%)Hx4|cur>PxL!iYQ#aix+-7`h5uwN|6__VL%Vwq|I^wTL0y;c8Qd%yznvE!m2tk`SZ{E0meXG@KM?uV!lx+h>jdshjtnS{PY^E!d zANWL!HX1?NHM1EHk_dq~bMnO4wo#14_N^ONtsW@$bRR$YR=Hfd@4@>VFZa9SC*MAE zGM_8%7~3{7GD47?o}PaH()sT0Zf2l%z3sQ-Fbp{$$2B7!wZc~Bx|q?FZAt*jaCK=3 zVSx!>vvy#^$m*Hd$vAAUtSr9t(xC%){ZYOHL?UC1BzH+gc%{cq_ z?|H@vj7>&Bh$eeh9#e=iX2_=#g1Hr`OFS9C_U@7&(>>rzAr=QM+Q*mk{49P>m; z1WbTPr_*p9T_FGb-S0sXW6a}rqZ!tU-GxxW+4CPRR)TbP@P;jWo`2!U(CU$tY5RUH zj9Qtj$z|JibiiV1WwFs}a8QPAwZa$?J>_|RJLUqyFiCqkjp-@NB7{gKl>*8WK{bX! z4+A+jH&-h!4-5ok z4Np(ZL@69BWL&fB_Le1XGP%vyl+Om_k4Zu`Fxz=FPQQZEf)skn2(cWxV|Kz?BE#+ z0C5~+oG@KWr_+9Ix#3qUm2mrx9a16FOy#l#!_?aCb}S;-%fw13KvF^+$2^fr0>c;) z8nhGF$=0jY{+|9g4hQ@C9mit2Wf%qq%q34^MHK+06k`U!!#HXMZG=!dovzpG!^6W- z6f?%cI2KCzt)^)^iQty)*tQD_2kmyyj-*ta3lNg%CP-yyj1on#`Ixq&yYupF8Oo+xz%L0IEwd%SqW6UrN$8mIB zCxm3OS zPy#xjM*%_r?H~w(03qb0J=d}PR@3)ef=kPC>V7Q@L(|eJ(j-WUfH6iX0|1dg9EV91 z1IR|h)F?W8=H%4$_|@@C8#auzBRFu!eH%A!B#0zD!9)Q8j6m0^m!d_Hq}okD$g~{~ z2yhmMsHtZRBWp%jg_3iI6+?&;Nt8fbsz^WrD2~Er+O`C4kmBigxX_kn$Pt?3HE2tgPjnW!X5fGdUx z!^BCM<>hjrP{?Mpw$p2yscbeoHkSR@fB)%&5BkN@(Cd@iGDx=ze`-GA({$J|VJ zK9jbs6o3SUsRY0sJ%nvL&6tUSGsY0XP8@ToDAfQXp2%1#iXo*_00B}8&J`CJ5M~%K zUYfdU8g3X$)9_wBa%8Z-x7BKG+_HN2-a9vK*$M!VgorBza1F+yYeiE*ht&cA!T_KE z1AqV^00@%b*H+m9n4Km=v`R~}Ggq6<`tRO)bKkyC#Zf!qD$_HtanlZ@r$A!b8RR8k z44^s`_7C{0{20p$00N|Q69NEq-j)iKii1$Mv!gqD!+N#R;+H2&1ASf0((Ft@F&zN~ zOi(k92_Y0?0Kgr!j$slI0);^-5C8~W3oQbPuZ{d4BPj_=0YVVA0zh2Xb-|;Rm6d!U zO$`G86i@*;jwLEJeS>L?YN#WIuQe#0?>oMf*HAWG=kVXRF`cKDAKA9v*=hQ|?Kn~< z5`-cM0Eb+HqUfsMz<>zF24Om541fS&1xf)%a2@5k=H34T)Rp>pA6@eSe2l~;fB;A& za0ya^l=6Q870>nGGR?F40000zdQ@0+LvL+uWo~o;0C?K-NX$#gO-xBu2+mI{DNig) z)lu-wOV(5H%uP&BRdCEn&PdIzR46mlGc@D^09dCESt!Wega7~l8hTV%bV^V~M=e8d zY;SUNaA9L*004N}GXekrGXOIY7Jg#@001R=R9JLMP(?>AQ(HV j004N}GpaDuF@#_M5qbjXLU~4N00000NkvXXu0mjfM6#qW literal 0 HcmV?d00001 diff --git a/lib/html/.svn/text-base/yellow_pirate.png.svn-base b/lib/html/.svn/text-base/yellow_pirate.png.svn-base new file mode 100644 index 0000000000000000000000000000000000000000..b6978342e3bbeabac5553aed45ab29927ca56eb8 GIT binary patch literal 612 zcmV-q0-ODbP)<&Fshlgteda3vl*+Y zYpk2OuAZ{1s)Mnizsa;Ew5GPZw@bghHNwCm#=tJSw}ZySCd9u=#>6(t$SS_F#>~nl z&de6Y#9zU_kgZR0jaXZP}L_w#xD_<;WTh;VNPF@(VM00001bW%=J06^y0W&i*H z0b)x>L;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_000McNliru*b5vAI49twVUhp< z0Ub$1K~xyiUD0h@f-n>Y;4`KJBdkrqkbFZWONXUQUTy#XUo{4pb??Kr=j_^ta{wA+ z)3ubB5!@N~QLjH>gT7(dUKe^M>dn5iBSY3xo4S%6Zz5zgAK8*chnvrtwCaC6)I02J zhi2Okq^)90viCo>(g-0$c>52QV=yK5wS`unh-BbMJe*FKzotVX4R9r)!F)bnYw8eC zf)Th_#q0I1r7AjhFkNwezTtdDu>mHb%aagkONbMvn1M$q7g<(xGQk7v05HnaqO3Y) z#wiH_Li#3(>iXTdmg$Z}b*rMyIW4=mD$W~Ux7^#tNt&zoYL)pV4#KA4BR@_qxpqn7 ySEn1#gB7TPl6>sg^W89Q*B?$MkBgbz+6q=r zd)7TyOl$Wm`Im<*dKEJ7QTqCisjFVuruW8AJz6mDM10etx}_&O*PQO#a9*_A2sE_y3o}dDbO`ac-J()L7Bz>M-56Ku9$MFN@F5GIeq~U@3xnjg9)5i#OHL*R>zT$3 zX~qo3#=!9W|DWOi8HWEe8UCj+{5NL!&%kizKf{?b3}8HTi(3~6Z~lhPO%jQ=wjpJ6bb$zYrYG8k+$&@7;4plYBr zDDDObK+=y6BK@#)Y-Wh$knyl}prVFtC+QKQOwV=`G z;ITDf6IR}uZn#)!%OXLQs}rS;YHhhBkbGi7=jyXDcPbvob~UeMxMedl!GXaV0Gxhi AK>z>% literal 0 HcmV?d00001 diff --git a/lib/html/blue_pirate.png b/lib/html/blue_pirate.png new file mode 100644 index 0000000000000000000000000000000000000000..162585644c1b09fdc75b9f90d5d1e2b4ee282064 GIT binary patch literal 675 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H3?x5i&EW)62LgOTT-j{%x&3>^%Faq=Z&9tg zpj38Ht@gB9)iJHQlX`VW&6$f8#v^B$$I|CqY!m2G-&{M4fb z^G?J!Evj33vUAPpz76Lko9~Lp&(mnRY*4<@cjg_ho+IAvyK{GZw@hfP*!!dI(BJms z|ALD9vU*of+kCot=IRAI&uPzmJ8{iHvn3zHw*F{ZygR>ry42)HO3iyzyH8ACwybOB zq}j{nY}mGR#rj#RH_qF%b;glno3?D5wteTU0|yT5-@hQg?{po|FZLxte!>4K0t|g> zHR3>{7?Zr+UF_|W4N8F=&H|6fVj%4S#%?FG?LdNM1&--J8XTewcaFY{0P2$Qba4!k zxEy;ix4KC|q~W3SnJm|iXRAE*vviB1b}r33tNQ~@6<%+9&a(To@0 z%qqOSWY64=lV)9BH&Y@neEb-Dn{(FR89K=VyTiN|cb`-3D7jW;>!cPst;A;by)$R> ztr~-#@*FuWFXti}B2xKZ!NQqQ<3iZ2+@p>Td>MvPymD^be1b;~Y`psV>#kW*5i^vz z41;#wo_l|#@Z92--z_}REnf_~7CflfkaA{Ua*n5gkbo;~_)czRb)^U;@Ic3MqKt7kQOVzW~)X7@#4pfPy5`njxg HN@xNAdc#!s literal 0 HcmV?d00001 diff --git a/lib/html/boat.gif b/lib/html/boat.gif new file mode 100644 index 0000000000000000000000000000000000000000..849efa04870113692b678f9680167d9ec4defa9d GIT binary patch literal 4347 zcmeH}_fyk}7RA3I^re@OKxiTKVj%Rkfb=Gz2L!1hROuZBfzYLeDn;oaN)n1jU3wRF zfru25uE^RziiqNK-=Fd3-TBPDk-~{-0(7!?d28}}lxZjHV z4Xz4}uEPBWS5;LtgTu{K{if>I%*=l`{xAP`;D5vc@l$#b0B`{c|K9U&CIIGhK%5zF zPP#pm&de)!d6INzB#TSJvc{a;FqQ{J2fdyoH%=6aUCf1BP@1MnWgI##Pf?nmR-jn) zT@AWhY?xFz;Uub;^s`C>cBRzYQG)SX}5m=_v_KH?n)-!;@bR+Wp{n`;H? z7@5Te@o99Ihaj5aG#S?}n=D*&Wof9(LvXa|PGa9@5IE)Rk;!*2oI~s7+Tntvn6P1n z+>u0KAu(lphRg{OyYSn9S5KPWKf1vNWbjCiL#v29^$4IwB@N#TS-8If3Xt`UMJ*`$ zFAQAIVzAN*3|}TjoTS`Ry=uK=**s^fxc=av+qN*6Q)D?RjU0UO{OW@@9*^e03Y^v+ zsP0Fpl(dw>&mN~qsdTJ-3X=oap`{;y+`8kX+Mm6Halxk*!V&8+1c*}{D7DUFPh8SD z0_1f(m_!Y9sg0~DC^MjA=1K*?Y%)dS*T4M~xb5W&$_8>+#Q@JM^)cUs+(4uggAAJ- zM{)blRlS!TP6J=9x@l*+8sxInm~6&@jE5ZE*5 zp1jew98RP{*K2BGOr5WJB*OdwMqtse^gKq!z!fmX0(8t_3)pLE;(PwrUhW41z_%C( z)?bhv=IrxEQ4^BI@;nC*Vga1rMY_aRrP3um7l7BCpPDQ+U;pc<`N}oM!yM_xNzC*W zY^QjPK{cPO)7_gIZ#3RvyXSIhTfrplt&V&8I}noJQD0zwYL#YV%Jvkt*+KCW9`EnP z9$uX3M-5a;EqnHUFmK(5`CW?reM|X5>PBom=nM9YNLTOOz6lg3b|^G1`hQCWv!@EJ zr5if)@%JBj=IjioneN?j&DN4MxV^cb=LND!ksgdQbutSF(VMsXga!9LUB%nJZwGQ& z@=buL0T#gBSYU#RzT0Ve`aX7v-_B#e45J=N#Hb`fn{zh*s2c=E6WiugUwk-B8OHPl zRWwlqr32SY)DAD2bXoh=u^$RcN>x+3)kND#yEht zEC%1!t-`jpZCSFDfB;^vAK7)X>u;{xN}Gi2mQ30B%(NuseBV3q{_`bAfGM5Z!IV&6jx7`MrY2rUEh~Yv#ms74ew^;&1_# z6xuQG_Rqtt`^BebPRa9#r)(2O8UAfy$OG@srs~uwb~1Wo-t`AC>!=U8r!K$f|EWrC-f zIXAU`&DunjIl?!rW7Vz;qr$Irdkwe}piFyTWGUKLO0;R+Y+9jZjBK7@)d$q+k7M~T zBO<#=)-d-xf3m8#R6pTxUMo8UvHT>&5n6)a>2VNml(N;-GaiG zK-_0lpc9og<)v)Y%sHI7croBfe`nCmg`oQ55d2^`uJu-+^_s#hBdrm3&e%3<{~Z-a z;o+fTGnVZ74rhiZPvARx3IFf}(-&Pz`w(T4AXls!waXVwP|m-XgzvX|2`TsgsBdEB z*Zt|@IKk~^E$Hvk9Osv*Co<*+y9YMZ3@7_p$Pl>&+)QPF1PZYFe&E%g^I<*UZ4Zxcx57)?c-NnTv70b@bH#GU{&U0X3I+Wwv zH|qI0Tb>RLD~t29GbF+@@6+h-ZR(W$dyr>}RIiDt)BB&FI7YU0Z6o>96&UP&W!bLg zCb-pwg%&(`BLC9W-SCK16xp03;l4p#g<^7I5N>zh!fSkVPKz2b616JDK3t89V}D+f zp8+T0hS)sj2m~iAN1nQ86UHi;$-(m(O-nZjb~R4k(NlZ1dR?GoZ(2HNkn5+!ozJho zd5u&#pXV-15r=y+%{j88y!#yl_r6n7e;Em<80#vkA!MG)KMpGsLe1=yFZf^3e|@&l zfg3BYidUz2(qiAq9QU1q2$Hms&E)EC-DhL*N)#KW{Ba~9G%_tVdVWux=4lxnG8i&F z57m=kN)nherRPw#Sn*8hW0Ym&q7X!nUmFjomL2QJ2x`Oz6+)`25zAO3$>o)^xsv!T zlg4|zigVB99z^8iW^4fKyr__i^pDdk1pSzHb>&>JmhZ&Tv%`^7*UDh6%KwN zCbnW13UuUTi3x#l4%OYW4y=GSGu~>Rm3cidBR>q(dDdLQ3lz)ujC|lZec{*MF@ByV z9o%c%HOSebUdGYqC4%b#4KTpgJJI*6i zi9@{Qa1P5Qq1CEQKGa)dhw{rAQeE*&6*K&5?~++hv{>9F{77;pi(1AM%FNS!^62N} z&Vs7X4M&0cdaJU9TX(d&bw~Az@g}Z6v9S^m+UL<1l}tNofaTtorf<8ThA7XPT8RG5 zh0aO&kTcj>4U*c*JI3F;a9h)S1?nXHieAUs&Oce-?!_N|jlKqA{DWNY@$q(6vIgFB z8ixg4KZiJ0r4m}zx@DNSy^_bOxhoXE6gwRruHvZYI4ivIS){{p@lU<6^M0=8UHwfe zt3O~n|9Uv@Qlv>(@abgU^r;)E)2$`(V3qELe}D+>T0J#UPRD)Uek$z`0mIIc%j_Cq zn6Ic{1i)b@_+bM``04_C7bM{5zcl6piT2tzb3K&yHTL&h^~a&(P;27x)fe#}(YEJC zSN7-CM|iZq??mYeglH-UB$dd=$GQ&0UGLCvpJ_or@hMCv`X4l$VetXfV&pkp9H>=g zq2vZE2JzQWJeVA?)#}a56b&+nr;??c4m1vTfhRaD4KO z4W_UP|M-av;-*S8^m7dQ zt;SD~TX?sw%D6FP8edER(}PTSHlnXNucQ>2kAw|8bk@EhYR6;C$HJYoFZ8`UCdL6i zQJ#)UcSJ?GxE!OuG8juQl@?WEf@N@47AVgqwW+i?F0`I$siY>ByB;B`zop5_6%e8+ zBT%BWe;}&Zthp3}P;AdLfa;q^`xiRI%-#^o#)@it+oaq`UI#ff9`JdneN`#-q6M`<{&Jh)oZJGY1dL6TngE)yO2Pr5B7MGz=-=N(t4h>X> zp{uUKdeuZsj2CIDi;T5}XxWkT2%+B(_@be*MT8_fNz{HQ9JY_@p|c#1f@f`DLhT_> zPZe_aYb1>D7l|mJA*C^8I18ylQd71j2i=h-j3F0vHb9M$nOq>4FZeM{^f&CPJo~g}<}FUy^0f9fHYpI?Cy& z?+F(U$}pP@T03`o1ez0gDy4)UMl?aexOs5wKeF&}75E8H!ZQ%^-W{YuyUP^6@I)IA z+cNwcBDAHuTy7Mle9)304qb^-cJWnUuGV$EVLW4GGPD7|X~^~W5Z^QzHd85SCFzHq z4612z8F#Dl8*2L$t=l*ZyJ~cqI?Yv-);U(k&^q9FI z?iIXV@E)rnFKroq#vn3iDZQzt)HTan@TfkN%*)zwpC^L9c8UK!MR=g^$_WDUcB+f{ j7K!ySiEV|%aYo`2BJ=2w`P|8nBr^0C8Ntm306PBxloWp{ literal 0 HcmV?d00001 diff --git a/lib/html/boat.png b/lib/html/boat.png new file mode 100644 index 0000000000000000000000000000000000000000..a471eb12a3399171ffa91ba969f5813a0c16ebf8 GIT binary patch literal 1627 zcmeAS@N?(olHy`uVBq!ia0vp^DnM+_!3<)>-FO0|7>k44ofy`glX=O&z|0Wf6XFUK z1Oj6qG6o{!G-HM|Aejb4X)}$DXQlyB+RT|V&!nZDnF&NQ|DQPn(K1SohQKfgf!36r zZa{A^mIV0)GdMiEkp|)EamTaeQrP?(D}396B8i|}ND!sER&l;+LwRlQfTN8D}6nN$7(a;j(lzVXX?CZ*UPa(Cum8*bHCv(|21 z*87z)=;@i~l}TGS7oXP6c7FFO=B!Sx#>)8<+2xJv)~K&qE}OQuy!5+~UhNw9KYi;y zOS3F!Vee20{VKFHb=twE-8trG*yQ%U!k9`@JCH!H0j|N4b|A`?%Z4Mns3zH}pYsRih2N?u!hd_x7p6krppZu zX6>5(My9H7lVU9IUxUuD0SyC(Zpc=Q4xRBeSq~ zp>m7$Zd!kuz@cToJfC$wm$=Nf$N574lU??wY~D~(z#_Q3%k697j)3CI^S&2O7%Kcc zZrJ+niQzjo$K-Qyw%a*6Hods5^v8RVhTXCQ8I3d5|5jCO+FB`a<;H!(d` z!7(Q}BQ>{Dq0CUv(2(o@ZH5*G4mLJ+HdYRHc6Lrq4lW)MULJ049!Vi#ei2zIc{y1r z85so?J#_^o9c39A4RcK$149!N6M1z@TMHu_J!2CiMovyn9&R29US0_!MHxjSlEME2 z41ydCmJBM)jEW3Qf{e_9jQ@`?E@EI{WMu>csP7mUnV4Bv+1NQaxwsh_m?3gN5+aQx zDZs$Q$jr>d!pzFb!U8m=7AVKSBFHMFXz0i$9GJ+iR48K9IB_9|veU+cqCpows2C>| zHF0u@iAzXIsj8`KXlj|5nweWzS~We&gn?hmRgVdHU@6i$mSee*O{!I+=-ug_(sNoj3o{`ycM__pIQVcG7gsx&I8WJ5`JxZ)(Y< z$6P)>ejV^iMU1&NwZZ4&qj}rS)=yY39}}=@nV4LHQ*oEwxp`lgo_uO^`qA>%_4!+M zwWBUR`kbFwFzHx4!;0KfcfK4v|2({9fhLc^ktYq=UAwP4hZI-6?_PY##x;G3?vlwf zuNg(k=-*FTz}V-{@KrW@-Q_#oR~_f>ZkK#6I(5k!DcK7zG}gQ3IG+D8_ldwEk(!++ zZI2dRzH>a6`E03@yNvtuiL+bfpKq^vxO&a0Xt`R}Q(5!Ox9mx{=xY1Qi0wdvs9ntS zWo67?v%X5(%-(T6OeIC|)|_8F@7Nu^V^=5f)gL*Yv0Xu^s6zB{yg=TbvtFl;Yi(4q z&(wI*@FsFf`U~;?wME`PdX{cHdFF`2Td7JN@q&r!7D1Nh?dE3RHQ8R-I_1(cOUdMP ziFXbv$*(f~7wGzZD3MsYZtF&M%SRJDwjKDVe73^;$Vyc^&C5!>nt}(;YW5Z%w!Fpv zH0C_WSot=SD_2detJ4CXJY6<}apG2ghQEG{-_BO6%{8A{E3|9U!8YULqI2%8WbymF zRrF4$<$TdOwKFD_eU3AJbeBDFdhCw9(UI@gKIKrZE{IqWdhF8TiRb)kUr+M8Dj`+J zeCeI8>dxKM?s+JeMl2H23`^kDajAu`XErtJ5vjH%5aGc%7aIe7d zXz@MMA2qZ7GqgWxyOy-Q;N2t!H|M!aa_o-xRZC5EJNI~Bd-%f5?hpT(s`oyanY)-@ zWQn+w2owL__|M7ze*G)GnC5OIB>&a>%Sw&pXjYN7^w|Fl#}EGEYT6zxVSE1nO#pCa BBU%6e literal 0 HcmV?d00001 diff --git a/lib/html/bottle.png b/lib/html/bottle.png new file mode 100644 index 0000000000000000000000000000000000000000..106101576ae3de4211c50a26363e7e99aa7b43f2 GIT binary patch literal 4335 zcmV*c!^Pg>?%wXo@=BWJQc3_o zL`05=h|maG(AqG@5rK$|G1gilLPSf3G1jOt#u#JPYNai3W{9k{#u%K=XL+7SVGICK z6wK#o97h0P?IFMs(*J$57KobF?^J6YZ(G8_BuEA_J8<;A7$wr0aRTxBm_VJ1dCvS43N2F4rHt`WbI8&Kmee( zCe{!c;s_GK;pj@v`uz@o{mys3^{G!iRaFI%nM@BjCjj-N`exxMyvrU#F}%> z*js9rDoYiB2Cx7I(Fl$JfB+1D126ysU zz9Gl}id#kwkT;sv6xNgtFrsG604NTY12jVv#nktMcRljZ`#Ze&)}f```zjdg`gs_<(cHIdRUBHL5d^U#eb))4>slxndQH|a09F*$ ze4Y*l%NrY;3`Muw-QM0tBrX_ZV69fFtm~q#3$4ny<*yxEl1MHGQx~Qx80mxEZL3OE zWV%dA7o>7kCRILHRgQE^ssORh;^4mf&p-L($E&IukH?63<;oQT%Q?B8lnz5?kQpwF zbZ9z0sEV}6W_dar?(b}GU2z1RJh2={j$qoFf=ofS)OChb`aXkO(3mO>5CE_bJ^sW) zzw~ff)=83VY;2_ITro z2<&y2{`jx|bUdC6hvN$u&i?Kvf5&l|)>dncz>P6vN$ILqmGE!fMg%92fCdD-^PTTF zbN2M<)Au~^z@5haR!ya_Jc2hQN@N(i1WQlt)Ph0K)Ml0061+cvw}%!|!+~ zO(sf}sV=qAb*+_B3`q#V7`MdIdEV`NyZht8u_Y#h(KN|Redhc{U=<)Ks>-6)RK;<~ z98WkN-NFZgzy|)TIXxc@w3OevM;=~Azoq+=m1T8~-lIox4n>MWU!x}6n5LO=jumMnn*u$*%#C1b3v z>m*5(QpT9=?d_r{?z-zPt@Y;SX20K`&1NerE3dx#>dMN>U@)-O9y@j{NfKj>)|!Zf z5Y0M{+kA#dO>`<<*L9^-rIa$D9Ldup_x;fGf;=n3u;mBQWISVxA!1P!LWuqS z{hghiPN$uwDHj&C(poddh^VS6*H!Fx#$yPC02Xwb&L@*enx@uTDP>*PMNv48Bc)8! zT-Dljy|OHOz5b<3mrk5GF&>YJsNe6OK7IPssZ&jVz&Upu2NA2P%JaOg>)XHp2*4tc zQdLo8RaFrg&bd-b2(h=fKORjN7P^zkY-3~V)Tw)uB>CR=zPGZnGMmlfIJVZVuC69Y zf{2`R*L6M5a~y|r4gf@S8(5_Z0E+;QD_l=XDF8qz)o!;P$8lX30LrqA<9M+*$n)Z> zU;XOt?ry8qYPZ|2>#nb_bIvD|iBgJ)thJ5jnh3z#I}>*E*-9zH&~LR`(h*wgC<+)8 zaU6SIP~>G4#m3Or{^ehvefHU17HD206-I<4e=Xe0Kp>jeSdFnuh;82()r>)eQ9TBXLWTo2!brjlrm*eDMd}N zrD^*0uYdi~$KDmkab4HO7_@dap7?$k1p(gD)p4BmLJNpxS#AwW(^M(thu)8$`@!q4zkcTIJw=fPLBJRrkLJcup66cBJzD>* zJ#8|dbB;&=P!vTF1jd-BpMF{h5k>L!>(|3DoXjU-*g|BSxj_&JA)Hh!E-tRGuU~lJ zez4@Zt`H)Xg)1FvExW}M0RRMq08MEy#we{cAU5qK;`7fx-|2KbFSxP4UlheGpGQ&b zI$oAlanuTeV03UJilXsws{Yiq6Vy6vdd>GT%j_HEI4w2efhwN^@LQ=ui_4U=TP zdg4@F*PQb(49}fAcQ79HaUi5?wbgpmyc{C-dcCqNtE#eEOV=f111}E&+I7>_Yz1_{lL0>5a0KV@V zr2$|%o#vVSqxpQEWm%f0 zVHhf<=JPosKKtzR7cX9n!w3vb4pZ74Tr`pKOYIxmR3jmyR z#@NEbg6q1|>C|zc*Xwk<-7pN(G&heh=rYC_6F+Ag88C5Ej-2QF{&YIs-QDf=L=;8S=`_nS!6oOMAs>&& zLa_4}&W^`JrD|LLIrluT;odtfJF1OBh~_B3Ik(o1$75s6gAYC!hOL9) zaB*pIp3V*q4m#~lt*W)PwZULf@DM~5-R?858BLF~E*F{y9 zq)V%(RX!^6TJSfww+kY_-JWJScRg)so@P-L1)-ZJqbL%~OYJI8R+a~@>r3fnC7rzI z3~xml)X-uZ!%YAMKmZ27GHVaplDe*yQca8y5s`5m@zP5#B2tzm+SIiwFJHclh}P0< zmN<@g>h61trYz0VG|#izbECDjyY_bXId>M?-Hnay<>lq0GYmE4NkoM9sLMoTj4>hD zY&Lu8rI*g0J=^auZf$KZEX2o;uP`nh$4Qe(k*jvQlO!4EuIqSNnjh?sk&EHMZ0(+I zE9$0MJ)f2fp%&aCA`vi#z?fi%<)1OeI7cCvv1&3M0@_YDj@zwPE0mHOV-5}u5K$>Z zL<{XjRa+@MYgo5C01$O$j~`zva(!*<#@dN{m9kCVA|N8Z6(CIz9u=6y#U8`inKP$f zeDQ_BV6eKn7RT}4-v09Pvb8qLvUa=O^Z~x-xUNG)Q5Z|tos4JS{PutD?CgK&iC_QF z;~zof3|WK6j@rlu##-CB$8kg}4nOkI58r#=DPz^OYnxX$-k45@fcomj<)Tc_oH^Ye zbR1V`Qvuqd%#1PCnipPp;k)1c{*Ny`_xaC%-WYRR=oe7vApxLB^8MG|cyVukQ=7tC8%0rFSp=umY6D?&Jjt@++V&3T z^1+MebzS}Jm6yBic(K=Ryx_WiQP!Rpe)cc_W@l%AcW>`)54`=0U-**mwH@gIApeWc zd>Vl)*(AxcL{%jq`HqOH(jW@XSyh!uGMP?CNjd}2j$n(uSXJ}7&N}UYL7k-&t7@Yv zv;c^CRwVOmv_B5~)+;Z+8HBxGe&kVOI5H*>IDkduj4>gk>-vbqI0+#L4CB$iz7%L0CwsvB}eg5KuH})r&-*{6iBP9m_(IgfE zxsKmzwE&IQ5$9-08DoeHA`%#-6j^L;kvz|{Mwwc10ahCZ*7toPOFko}OthDP=Fd?d^Z~2Y>W$-~7hQ>#yB+-+fKt1pvIsmKp#Ev@r&m;m8SW zt!iUza|dUwHJSi`n06i48e_GQjv#HUQPOdM$VGM1kE$>00^aG}3^;-qd=wAM}eV2s`TU-QX`@Nl_X#2lU-!CH_UfPgF@10bkN z<%ALd=4sN3S{5t;YwmD|lT1p9yR#(leAnRufB*ymAlv~!c=!tJEgSw{a>!!ALvL(va&vHD zV`TsUc-k`p001)pGZ7YkV*mgEC3;j?bV^V~M=eufZE$R9Zf7lKVPkY}a&rIxc-k|n dFw`-GU;q(%0_Z|{Mrr^6002ovPDHLkV1fgmIA#C< literal 0 HcmV?d00001 diff --git a/lib/html/brown_pirate.png b/lib/html/brown_pirate.png new file mode 100644 index 0000000000000000000000000000000000000000..2db25e98a9f8bd9f083e74df19479ee52038a018 GIT binary patch literal 612 zcmV-q0-ODbP)xUFmW4?VkEfFE00001bW%=J06^y0W&i*H z0b)x>L;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_000McNliru*b5vAIVy(^EN%b* z0Ub$1K~xyiUC~inf-n>Y;IlDvibSFqItH4gq>Th6A+-Jff7KX5*1L;s-`TT^a{wB9 zKeUvOE!^4nQLjH{g~R?Geg$vEe$0*cQRx&AH|Yor_Se2TJ=8(>YWa? zQ?nh$GFGuA+4~rJ@^uP+Cr;OL^6mZ9y-qKmop^NfKU=@+O|E>)WM_# zBM7mICzJV&s^~bvbjta%!}*kA1AIbf8zIsgAvT<11__~De8xm zPDue!&}UIzuHTJomCi|4w<;Sg)2fXx<#OcdmM1GWNRR5hTGeswnnly_t>*^oqqYv* yr>Gszi)E^U5Id04~t#g+()a`1F<46!(!oS-0P zP{`r^VWabnHGhPf-V`JX+C5vi)}zB9A>l!)B1=P;3TN{}xfzSQ+SVyCHaSf3a9!N! cY3j{B$b_ifK(iP;UHx3vIVCg!0G0q>zW@LL literal 0 HcmV?d00001 diff --git a/lib/html/complex.html b/lib/html/complex.html new file mode 100644 index 0000000..c8f41d5 --- /dev/null +++ b/lib/html/complex.html @@ -0,0 +1,237 @@ + + + +Cartagena player + + +

Cartagena player, Complex form

+
+

Number of players: + + +

+ + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Cell + Cell + + Red + Red + Red + Red + Red + Red + Green + Green + Green + Green + Green + Green + Blue + Blue + Blue + Blue + Blue + Blue + Yellow + Yellow + Yellow + Yellow + Yellow + Yellow + Brown + Brown + Brown + Brown + Brown + Brown +
1 + Gun + 2 + Keys + 3 + Dagger + 4 + Hat + 5 + Skull + 6 + Bottle +
  7 + Keys +
13 + Dagger + 12 + Bottle + 11 + Gun + 10 + Hat + 9 + Skull + 8 + Dagger +
14 + Bottle +  
15 + Keys + 16 + Gun + 17 + Hat + 18 + Skull + 19 + Dagger + 20 + Skull +
  21 + Bottle +
27 + Keys + 26 + Dagger + 25 + Hat + 24 + Hat + 23 + Keys + 22 + Gun +
28 + Bottle +  
29 + Gun + 30 + Skull + 31 + Keys + 32 + Bottle + 33 + Skull + 34 + Dagger +
  35 + Hat +
Boat + Boat + 36 + Gun +
+
+

Active player: +

Actions remaining: 3

+

Action to take

+ + + + + +
+ Gun
+ Hat
+ Skull
+ Retreat
+ +

Selected robot +

+

Players' card counts:

+ + + + + + +
3 (active)
3
3
3
3
+
+

Game history

+ +
+ +
+ + \ No newline at end of file diff --git a/lib/html/dagger.jpg b/lib/html/dagger.jpg new file mode 100644 index 0000000000000000000000000000000000000000..e8e1f372360aa88bb710ad0e637660657aced181 GIT binary patch literal 1396 zcmex=<;H!(d` z!7(Q}BQ>{Dq0CUv(2(o@ZH5*G4mLJ+HdYRHc6Lrq4lW)MULJ049!Vi#ei2zIc{y1r z85so?J#_^o9c39A4RcK$149!N6M1z@TMHu_J!2CiMovyn9&R29US0_!MHxjSlEME2 z41ydCmJBM)jEW3Qf{e_9jQ@`?E@EI{WMu>csP7mUnV4Bv+1NQaxwsh_m?3gN5+aQx zDZs$Q$jr>d!pzFb!U8m=7AVKSBFHMFXz0i$9GJ+iR48K9IB_9|veU+cqCpows2C>| zHF0u@iAzXIsj8`KXlj|5nweWzS~We&gn?hmRgVdHU@6i$mSee*O{!I+=-ug_(sNQ_-W z4u72Az5k(vSfeh>qk`klXP=UYsaf#g>AtMnAO5YIwQfG^lK@YqeF4XnnL<_`d{W@% z9@Xcqs(9ne4TFT9zS*vxU9Y^Ic=83ebT3V0^HjFEKL1tJ(`nP6=sxLLwEOO}; ze(Dm!!1#t;eyZ>#Lsz-R{IgpkUvCN7dTE=np^aDB%PjYR!+Q&)-bB^hs+oL0@_509 zW9KEN2safr{+JVP>Z>Kv$L{&z@71ecHfLV{bkK(Dlex*6( zyb^eMV%C(W>sVUa?n!QY-r(`7>S{>lk}g|2zTC&p?KM_vmu{Td{;2itlEy9b=aj71 z`&fEpL$oW?Mt$RV0qiV#hW{D*r~EOFxpZx%cTzpy%SZR!BmJHlKUl~9LF178k<|SU zLigtCrMgVXJ1FsJ7B4Lf%~ zY}@^xp(Wi<{M)8mv1_z89X8pQtj)(4t!{a&Uh3K%+w4V0?h4%c&#+Sd`CfnDmNG6) So~HP{KZ3PgH%`9u|0V!Z%uuiZ literal 0 HcmV?d00001 diff --git a/lib/html/dagger.png b/lib/html/dagger.png new file mode 100644 index 0000000000000000000000000000000000000000..0d4dabf5f23712ae7fab2ed77176351f10655ea2 GIT binary patch literal 4590 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 Y01wjH`oO)I6u4o3ERqb;Vz{ma13c8nv0K zqNkU#o~F5*u3+9ile>e1$Z)o%t8m;-zoE8W?JJMTe{$kfoxzc8@i&>viFE2zozIV> z%a_KozJlsztInW=@n?khSC02>miKv`{D7qXh#YVR-!GYR00001bW%=J06^y0W&i*H z0b)x>L;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_000McNliru*b5vAHY0gThy?%u z0Ub$1K~xyiUC~inf-n>Y;IlCuV2UYAL`GI9mf%nlLbUz=f7KXb*1L;s-`TT^a{yW^ zFR+x4HQZVCQLjH>gT7&eNf#y_>TSNXBSY5PH4P*?-ekyVKMEzw4mY10X*K^OsCU>k z4(+xZ$WXEu*mRufFUKU(fIt#zk4B@hrVchG z7(swlJRZ-lR7J-TrW4NN8_p*b8{iQ--3pOk39;oAGe`*KqAaUUDY%Uz07iLU)#pxK za!Lw-fU>VJrd8Q6&g(8dSMku*El-wynxE8rwd&K}w~MynYu8WrCvBhl yk5M<;H!(d` z!7(Q}BQ>{Dq0CUv(2(o@ZH5*G4mLJ+HdYRHc6Lrq4lW)MULJ049!Vi#ei2zIc{y1r z85so?J#_^o9c39A4RcK$149!N6M1z@TMHu_J!2CiMovyn9&R29US0_!MHxjSlEME2 z41ydCmJBM)jEW3Qf{e_9jQ@`?E@EI{WMu>csP7mUnV4Bv+1NQaxwsh_m?3gN5+aQx zDZs$Q$jr>d!pzFb!U8m=7AVKSBFHMFXz0i$9GJ+iR48K9IB_9|veU+cqCpows2C>| zHF0u@iAzXIsj8`KXlj|5nweWzS~We&gn?hmRgVdHU@6i$mSee*O{!I+=-ug_(sN)7VX}7OZSLq42NtaPTAZ zy_Y<^cfNhNt-8B&&kiNYvgC%v_crbkUH!^x@83JyIhqc{2<9C$GYIgwQCna*`Snqs zh)dt~s+}HZ9ABziwzkJF`{>d?GngKy72UZmTU(#MJZk3q4xs4U0Tyly-zx5Zhm1N`}4C^;q4|FTP$+d=uVl^^DbTc z`;EWP{=C@a?&^A4RQU4scl}FsOV{=#&-|D&FJM~&r@}nO_4D_{C+?4%bjo`6>lKd! zB4R8H*7m`gkF^&}(%J*C^@mwt5hPTd_344>_(kK8(Q-PY=W zvJ;=JHMKYt8?*{vIIiRR&mekkkKU&26SqIyo4nkWWmf9~-a-Sx-L6cBl$2NH#4)~{ z`<;lGddW}KgtIjzQUfnDQrkIBB$=Zn>6ycR1v#&CdP z-tv3vUPm3SsMAT)FxAMt7o)8l|Un$QBtCxMfAX zi1wgjyyGK@{YXr{|tvLlgj?=;VFzcw)sKgBikxz z?=6#bxz4hz5&UpHSmxt|B2fqVpsY}lxyQwmclQcOOfr2K=+xj>A@Zg_>fVvNg3 a?W1PRzN5S$&?7ao?&q?w?qs$4|2F}42u|q$ literal 0 HcmV?d00001 diff --git a/lib/html/gun.png b/lib/html/gun.png new file mode 100644 index 0000000000000000000000000000000000000000..b8c8237a0ed7a6dafebaa926ae59cfa31c2d8f2e GIT binary patch literal 4686 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 D)sS2Z literal 0 HcmV?d00001 diff --git a/lib/html/hat.png b/lib/html/hat.png new file mode 100644 index 0000000000000000000000000000000000000000..94a7ad56ee7db546666ec23d6dc653aa6fc39bd2 GIT binary patch literal 4561 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 literal 0 HcmV?d00001 diff --git a/lib/html/index.html b/lib/html/index.html new file mode 100644 index 0000000..a046414 --- /dev/null +++ b/lib/html/index.html @@ -0,0 +1,59 @@ + + +Cartagena + + + +

Cartagena

+ +

Changes

+ + + + + + + + + + + + + + + + + + + + + +
5 December 2008Updated the libcartagena libraryand added the library unit tests.
24 October 2008Added the libcartagena library.
25 September 2008Slightly changed the interface description: different file format, and program now has 15 seconds to report a single action, rather than 30 seconds for all three actions.
4 April 2008Initial version
+ +

Cartena is a simple race-based board game. You can read the +rules and look at the board to see how the game is played. More details on Cartagena are at Board Game Geek

+ +

The challenge is to build a computer program that will play the +game well. The structure of the challenge is heavily based on the one +for Pousse. What needs to be fixed +for the game is how your player will be called.

+ +

The Web interface

+ +

Here you can find some simple web forms that will allow you to +interact with the automatic Pousse players. There're two forms +here:

+ +
    +
  • The simple form, intended for your own +program to interact with; and
  • +
  • The complex form, intended for a human player.
  • +
+ +

The library

+ +

I've written a Ruby library libcartagena.rb, to act as a reference implementation. You can also download the unit tests for it. Both the library and the unit tests are released under the GNU General Public Licence v3.

+ + + diff --git a/lib/html/interface.html b/lib/html/interface.html new file mode 100644 index 0000000..d9d76ac --- /dev/null +++ b/lib/html/interface.html @@ -0,0 +1,123 @@ + + +Cartagena Interface + + + +

Cartagena Computer Interface

+ +

Changes

+ + + + + + + + + + + + + + + + +
16 October 2008Another slight tweak. Introduced notation for the card played for an advance move.
25 September 2008Slightly changed the interface description: different file format, and program now has 15 seconds to report a single action, rather than 30 seconds for all three actions.
4 April 2008Initial version
+ +

The game will be played between two and six players.

+ +

Each player in Cartagena has up to three actions (advance or retreat moves) during their turn: your program will return just one of these actions.

+ +

Your program will be run afresh for each move it has to make. Your program will be fed the history of the game so far on standard input: this is likely to end with the previous actions taken during this turn. Your program will then have 15 seconds to report its next action on standard output.

+ +

The format of the input is as follows. The first line will have a single digit, giving the number of players. The next portion will state the layout of the board, from start to finish. Subsequent lines will give the moves played so far in the game (maximum of three consecutive actions by any player). Next will be a single line giving the number of the player whose turn is next. The final set of lines will be the cards in your hand (in an arbitrary order).

+ +

Postions are numbered consequetively from the start, with zero-based numbering. The cell is location 0. The boat is always the last position (number 37 in a standard game).

+ +

Players are numbered with one-based numbering: the first player to go is Player 1, the second is Player 2, and so on.

+ +

Each move will be in the format
+<player number> <location moved from> <location moved to> <card played>
+(with a single space between the fields). The <card played> field is always missing for retreat moves, is required for advance moves into the boat, and is optional for other advance moves.

+ +

It may be that a player takes fewer than their three allowed actions in their turn. There are no additional spaces or blank lines in the file.

+ +

Here is an example of a valid input file:

+ +
3
+cell
+skull
+hat
+keys
+gun
+bottle
+dagger
+hat
+gun
+keys
+skull
+bottle
+dagger
+dagger
+gun
+hat
+bottle
+skull
+keys
+hat
+keys
+bottle
+gun
+skull
+keys
+keys
+skull
+hat
+bottle
+gun
+dagger
+bottle
+skull
+gun
+dagger
+keys
+hat
+boat
+1 0 4
+1 0 3 keys
+1 4 3
+2 0 4
+2 4 3
+2 0 2
+3 0 5
+3 5 2
+3 0 4
+1 0 5 bottle
+2
+hat
+skull
+hat
+
+ +

(Note that in this example, player 1 elected to take only one of their three actions for their second turn. It is now player 2's turn.)

+ +

The output of your program will be the actions taken in your turn, in the same format as specified in the input file: one action per line, in the format
+<player number> <location moved from> <location moved to> <card played> (<card played> is optional for advance moves that are not into the boat).

+ +

Player programs will run on a Linux machine (probably some version of Ubuntu Linux). Exact specification is unclear at the moment, but it may run on a twin-processor machine.

+ +

The player's program must be named runme; it may be a binary executable or a driver shell script that starts up the actual program. It will be invoked as
+ +

    ./runme

+ +with the program's top-level directory as the current working directory; libraries and other support data may thus be accessed with the relative path name
+ +
    support/...

+ +

The program is run once for every move and then terminates; the current state will be passed to the program on standard input. Persistent inter-move state may be kept in the support/ directory if desired. When the program has chosen a move, it should print the move to standard output and exit. If the program has not done so after 30 seconds (real or "wall clock" time), the program will be terminated forcibly, and lose the game.

+ +

There should be no child processes alive after the top-level process exits; the program must ensure that all its child processes have exited before the top-level process exits itself.

+ + + diff --git a/lib/html/keys.jpg b/lib/html/keys.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a0f655ea043bdd973170af4c07ee82b5a4871c98 GIT binary patch literal 1472 zcmex=<;H!(d` z!7(Q}BQ>{Dq0CUv(2(o@ZH5*G4mLJ+HdYRHc6Lrq4lW)MULJ049!Vi#ei2zIc{y1r z85so?J#_^o9c39A4RcK$149!N6M1z@TMHu_J!2CiMovyn9&R29US0_!MHxjSlEME2 z41ydCmJBM)jEW3Qf{e_9jQ@`?E@EI{WMu>csP7mUnV4Bv+1NQaxwsh_m?3gN5+aQx zDZs$Q$jr>d!pzFb!U8m=7AVKSBFHMFXz0i$9GJ+iR48K9IB_9|veU+cqCpows2C>| zHF0u@iAzXIsj8`KXlj|5nweWzS~We&gn?hmRgVdHU@6i$mSee*O{!I+=-ug_(sN!eZS0b&Ydvv4JEJ)BH1+EY)z{us^)|aKy{D4I z_Vji0%g9|TJr~Wfy>h&EzrV!aH+6G0AALGAPo-bwQGZtFQ?(4yz9RQali!^F`|Qu_ zyfSwsi}g}JYVO3`$tu|GU97w5pp1}!a_rjO1-AV^Lb=Tnb2=@K9AG$5JUOm3dR5QM zybm62pTjRdn&9@`^~T&q8WVP^UvBLAXS?d H1b8@=-tCNVvbF~6(a#PO#1#+Bm* z1(U*Uc|Ei^UUrpMX(vn1`OnbB)5PIaa`NOl;mE5>?f!eZG%wscE~qVJ|DMyf;p?%w znWD!-y$4zZO4^9=Sj^0nW^7In=j5@y?gn_H=d{XijP|DRo!@^P+riqy4T#VC-Tszm|Y$h z=kAmYoWmbx+rIHn&Gdu2>Nc*n+59>#q0jP~$C)l;;pWNP?|iX*JLhxPvlW+eZixl% zT)+O-ox87#W|yii`E-?~+KEkJn~Y3RK~YgfV(hZ%OK#r0=c?lM@al$1HcL;$JI-sK zQyBBg^XBZ3JWb_`4iA#1?G@d4tazSIn{{`@!l}$hp7U_5v-939E8(7P{4;v?+gMaw*e^!&oXCvhaaAAxGcc}O>_5ZwjmPWx zvzy$s=S|x7?CafIv3(y(M7F+K+ZR0dW5C~|J5?HYGax?jG+2-Ri)5QfnMK)~+^9;{UNuBeT+fwd# zw#O^&%ykDdl`Pzy_2)TgHSkQTpH^G=I$%0m{&9TNV8#msV_)|coT40h$ tV-L^Ow}-EU2f68aJt@2YO88~a1@44JjZ@SAGerIfDP45q=)3y=Hvt@5X+{75 literal 0 HcmV?d00001 diff --git a/lib/html/keys.png b/lib/html/keys.png new file mode 100644 index 0000000000000000000000000000000000000000..df6147f25ad2567a94fb56aacf5fdb39b9f20d54 GIT binary patch literal 4970 zcmV-w6P4_VP)R9M4hSZRC*P`+NQRb@$!!W3)i!R#(^U?&^E`+&WwfB-NG(ttD?BV#B)yWVk8Sxg;x?%8^?anrtA3&W$yT)_a*o9`K|RKyum8VDf> zXuud_000pY5CF8%06>sz&W$mM6p$DIW55_gqCT8Gvz*VT<0yFep$8uS)z57z{_98o zzBGSa_%(zGO@t6rR2BmS$%rN(5d;BAQBhQzmk5G@pm<@9CMF;#DhiT;=BO=VnCrsq z)^QPZfA*6f&TbibG~QOnTUDl~u1U3=qa+DP?wk?DpgjkkkjEUozQtBnLjynKAFa1L31?_}K6G^ArX&!v# z#r5*?b-Q;-(L29X@5I`2T_=(Bd%akM>2w+mdB3qZjuFwaEXQ$#5CCA?_NFn;c`_M| zO%wnwL=6*tnQ$|m)rFIXk7S(GzFTj3_Sq*(>CD>VB6E|PkY1o-q7|6P$a<@mw5Y>* z6o+0W(`q#+rL}5JYj7OLb&@u>KzN?*pa$LR6Bet`XBGk>V30X31EUug&mDOAzztV# zyJE+-T4VS6#zwPMO53VI>q+#uAO4c8HY z)o-``fqY?Npn#NIM2H~(U}P+`=tlvyKoL1mDj1^b?WQu)buDfYZDOS()G-(tiKy0U zcKW^bdNl~cTBD%>xMKq)$#lBg51M|5C*17dfSXG<{0=gD369JFAZJ%!d(GFr{`aMv z9c#6^vDWA|l|u^sAV+4Ln#27v(p0I^c(`#$qI-CbQ-np<8gpFO{{J(KSAeA{nZw(EFF#+VXX zYcNW4#@da#ZCSn&gV{W%)O8Y^P`}aYG@5QA!x-zfJDqe2$53m1iB*`?Kn#X{FX3Rl z(UA&gwqN09)0uq1^%6pApqeXXlnTRuXwq$Vr$#4Blj*dZs8%bC($gnSuB@z;hKF@gh()KmvUpU+T`xgjTy>e|Z6`N$6_rAaU4NBx=E?RVULmmi9AtIL@}F_lRVjm~)K zqU~fjytgrvaxH1Lvc!m=0`3Wi)FZ-bzeisLwnqBxE%#w+D@ z+jcp(+szgQj6?C#ffr|{W-4pt_fDP&MD)ckKlIJ-ep_(%zi+&j9~*w{@LN;cX7_#g z*5~%`e}4b|;dH*3$!QU9W&;4g$OtgXLQo=DJa=k+ZMiSHLPgX@q?k;&9Ej-k)7&|B z_~^Tb-rl=s@0*9-did*K0Y+jYDwWE}$WX7<$t6;~cISKl^8GE7Q`hX;{nEi#XSQ#< z`(vNTj7&2t%NPS71i=tQ0FY9KVHib`5W;9JV&zy)v)V+BLAUQY$?|HYm@h5NpWC%( zFXdJ*ieEkSW~wx>(x^7$;K;dCg|X6K|Lxzj+wJno^0x7b^8Dh^*f=wIDd7QaD~zyyH)vIxaW5Lk6waKSd-KghKmPGgpZM*QwMKK-u05;k6#?20 z`=#NbqsNcU&d!dFjRal4IFMgHclPZUo~M*jN&x@?FW}99HUu_>3S$;yRve04IyW{v z)~L32U$@8Mo`u%&BPTw3*Pm{k-nzE7e#^ew{I-u$GXZ18(m*0zT`l{q=Ir!T(D&Qz z=C#*fhZez$3<3ZIz!*>fz!-BJ$8}wsTNJ63GM&zzTUeZ*Tkt!*m8BKG)7v()EfVpE zZ@=BK+<$oFYY@sEV^dSbVd6)!-TRYUZkN6AsVAN+m5L)<#@;=7bZBBsXrcAx0Eq!4 z2hchyFQ2V#l%vp(;=Y%3&dx9F-~XHiBx?B+#nCO}6AalC&poyG<_~R|nLKm$OtV$D z8QXK+RXexuSU7onW4&_t@Y^@-yLn>k>~EiXa%5)ek$?JD&nL{vbIt+C1!Dw6&~G-I zkh*lf&_ptVyKqFvb@#hg|X7;>iMN! zv-`>mFIQIAUpnvxCy>vl3z^J=4}Ib5kA8J|acN|%6ogVS zZFk(Uw0eH}ifvvp#Vof~^H(Yx*9>oYU-AT|C0N`svR}*=T8&DhQh(#%!TFPCj=u9w zA?^12k(O$Bc=)l$9{bq6_fAevu9sJA&O5E<=Rbdc(D9>wJTyLf=;*OeJ#c?+bi~Uh z4WU|L1}vkA00kF81^|RH=yrUhoKo7X)n7mODnZk3HJXjk<}8y*&1{{K5cg%U`=&jm zkwPJt(vdI1?#l=Ew_0_}VRzkqx1De+jhY|!kDNHVytdr#wGD;B(10=$-XHn~A(>8e zS`BLR?|kQ<$Hqqn3;7c#PG^#^w$_y<9vT~5tt@}){!gzr);5~um8H3nV&*Gf{(=;N z62UJY``K%+zdTUP0)b2}oysH!hX)nNdTS$&`<~});_71Z1duB7(uvcjkERN#M!R<9 z)|q_Kb#3>yJ9hS?C_k9F`@WCPFP}kXyp)Gs618jRm(~Y!satQlzERt_>iS*#{^-`O z3Ih>P27r=VSS;qF*bn-hlu>}@0$oKamRe9srlu$Fz3-ldbMy6P?a^;M`rwxyB%JQp zx#P~e@3b7|BrNKZYNLWirab!(ufMYA+N(Ur8p!8do3F2}R%;a@qM@O|Cp$wboZeuex;Ny|J+o%IWavh&g;D67kB)3L73| zzzpGp+T5XpqHVj>vcZsx!3a!W6vvSiaU267h0=+WjSujvJ>ww*9f~P`;}6D*bm|m zI8A0V+;tYtodrUAU4LX`L`5+(I6gcQv^t$uI~IaC)_S{XWiyFf78uvs=vav$7Jeim zsr6?52aT7@LqabWjg(RaCQ?!uO)O?5y@}~9&1Q38U?Ayujg4B$b{9`CIFuK%#Yn0| zF2A(CJ~TC%C=MjDxkNJU*d7OBgcLy-_xfQJMREKA&+t-_N(f<85fGqbtwJR%$0im_ zrBX}h&Rx6ns*>K#Pstce5QR|~ihi(p#G-FNBSQdvIU2Pz zNHh=w0EAK`L~0Xh>3E*s?0TLzH#hgwAN}XedvBaOeIn=Dd$(_U`nSKk|DgxZEw8%i z{OI(oA4WkKF>Whm&;U@-ptM2*5uy46#>fTVkW$KQi`kY00fxZ(r9Ho9&Y}-0d@O?eVYH&6;}gH(et00w{o0w4h(T45Z^xG(!M)QY5&xifQf zqXWaQy!D6r`S~w?{@!BRmVL3XurxI_YcsdJTKUV*ePO-jpFDYHbYgsMwK6Fq87M2u__iGEy4)*0&ye>G^}_mnw}`Z+7dIT|XM1n5}NKl%(0r zK-iZf!=s#V&be(_GKw=^vRSL9Tt`ZYhzP(bBgQDLB>+-laq2L1C^C!jfq`6cFmw2w z-~Z;RUr)_UfBU;%z5jtPIjKUe*_)W2U0T`Ld&5oKO(NlqMiUL`G&|d7W*g;N*zsR{ z_SsIoHk{87<@0Gbfq+U$B_tsPH39%ZdN+OuK3=6eBaBb=2up} z^yoK!@tdbNeE<1_uPv3!*WGwyUkHPcNF@Ov3IhtJ+o;Z;I9gtq|M?GpFqBS8zl)#& zfKqCVVF(v620#hgV7Qc8gX%{?v)7&+A3t?`ZuhM>UbA)ApMUZ*)$;l)ufKS%ygYN& zmEiDNv%y_2Uo7~oX1Ae(6OTqOL~daXYc>)>4mwW@#!;*^LElD7Oz$-p2b^@ zMxu}_74log$IhNQ^>+_^v9i2$=*?q4_~q{`uW-+O_W>fKpeWSr2h`X^B_u^(3oceN*&X0Fv0b1D@F^DJ_*^4bSez#>&OAAk69h*>Q0Dwrf=18dPWD%G)MM8j66UVVqh7g8^C<%lxZdsv*>+8KnBT>kMl+0YrF95(g_jPE3dBf{Pqnu?JJIMNu+WbAdEt+cdE8+JC+MT2wYzNvH<)4!x*iBkPCjBi{eRZ zpagn;E0Iiq(SQ;RHOL|j3JfB$ZX|&@3@8KCmwKKbw6wY0rM*MM_d9(_%WwAA6Upqw zyS72l0E~dtCJdwh19a@Ufp3h{CIA2cJ$h7FbVF}#ZDnqB004N}^GM7~$xTd2RS3>c zD=AMbO4U*D%uCi&@XSq2PgQWtNzO>ktyCy8)H5{X0svU24p}J3-Gl%D02+E!SaeEI zMMo_|Z)|UJb8ul}WdHzp+A{(G05bqH5f*-90000bdQ@0+N>D{dEmL7_aBOLAXDw!7 oV{~tFa{vH%+B2#!)G>r$01TzcoL`Hb9P2SnVr9)I3MKOIF@KNyJK4@i$STU}fA+T$gWk#b0KtYjNUKUg}g| z_g8JmaCYiuZ>xicnTwh6XK}lOi^+e4_icKom!QjukD;5Vo1d@ud4$1{m(P!smZr9z zqq3Q$wfumKo2t3Xmz<`lw*H8dp|QWtpro?9#&8A=nBzjo00001bW%=J06^y0W&i*H z0b)x>L;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_000McNliru*b5vAH!$Sy_?iF! z0Ub$1K~xyiUD0b_f-n>Z;IoU6cqv0=c!Le&CP-4I7P0UDtr|njdiG-5|LnIH=K!>h zapWnVXSj2m{+?3d8KPn_I_cw2;^qPMv+~4mS z`*z!{WuanE@|Qn+Y1B|-b@BFMVNB6m0*KP=Otz5f8ffiwI7u(|dC0000~bp9zYIffk$L90|Vbt5N34Gc>~nMz+U3% z>&kwEhh3C|_n3592~f!1)5S4_<9c!eL-Q{-Hb$OHOwDT!D*RtKx##~y&u9O6GgN;6 zXU + +Cartagena rules + + + +

Cartagena rules

+ +

Changes

+ + + + + + + + + + + + +
22 April 2008Revised deck size: 17 cards of each type.
+Clarified that this is the Jamaica version
4 April 2008Initial version
+ +

Cartena is a simple race-based board game for 2-6 players. It is based on a famous jailbreak by a group of pirates in 1672. The board is a sequence of 36 positions. Each player controls a group of six 'pirate' pieces. Initially, all the pirates are in the jail at one end of the board. The first person to get all their pirates off the other end of the board (into the 'rescue sloop') is the winner.

+ +

These rules describe the Jamaica variant of the game.

+ +

The board

+

Each position on the board is marked with a symbol (one of bottle, dagger, key, hat, gun, and skull). The board is made from six segments, each of six positions. Each segment contains each symbol once, in an arbitrary order. Positions are numbered, 1-36, from the start to the end of the board. Each position can have zero, one, two, or three pirates on it at any time. Who owns the pirates is irrelevant: pirates owned by different players can be on the same space with no special effect. All pirates start the game before the first space (position zero). After the final space (position 37) is the goal (the rescue sloop).

+ +

The cards

+

There is a deck of 108 cards, consisting of seventeen each of the six symbols (bottle, dagger, key, hat, gun, and skull).

+ +

Setup

+

Each players pirates are placed on position zero. The deck of cards is shuffled and each player is dealt three cards. Players can see the contents of their hands, but not the contents of the other players' hands.

+ +

Taking a turn

+

Each players takes a turn in sequence. During a turn, each player may take up to three actions. The actions are either:

+ +
+
Advance a pirate
+
A player plays a card and moves one of their pirates forward on the board to the next empty space with the same symbol as the played card. The played card is placed on the discard pile.
+
Retreat a pirate
+
A player moves one of their pirates backward on the board to the next space with one or two pirates on it (spaces with zero or three pirates are skipped). The pirate cannot skip spaces with one or two pirates on it. The pirate cannot be moved back to the starting position. A pirate can be moved backard out of the rescue sloop. The player draws a number of cards equal to the number of pirates that were on the space before the move (i.e. one or two cards).
+
+ +

If the deck of cards is ever exhausted, the discard pile is shuffled and becomes the deck.

+ +

Back to the Cartagena page.

+ + + diff --git a/lib/html/simple.html b/lib/html/simple.html new file mode 100644 index 0000000..4142904 --- /dev/null +++ b/lib/html/simple.html @@ -0,0 +1,76 @@ + + +Simple Cartagena form + + + +

Simple Cartagena form

+ +
+ + + + + + + + + + + + + + + +
Robot input:
Robot: +
+ +
+ + + diff --git a/lib/html/skull.jpg b/lib/html/skull.jpg new file mode 100644 index 0000000000000000000000000000000000000000..0575e00f14fc333bb5dee79eea132514c35a229a GIT binary patch literal 1478 zcmex=<;H!(d` z!7(Q}BQ>{Dq0CUv(2(o@ZH5*G4mLJ+HdYRHc6Lrq4lW)MULJ049!Vi#ei2zIc{y1r z85so?J#_^o9c39A4RcK$149!N6M1z@TMHu_J!2CiMovyn9&R29US0_!MHxjSlEME2 z41ydCmJBM)jEW3Qf{e_9jQ@`?E@EI{WMu>csP7mUnV4Bv+1NQaxwsh_m?3gN5+aQx zDZs$Q$jr>d!pzFb!U8m=7AVKSBFHMFXz0i$9GJ+iR48K9IB_9|veU+cqCpows2C>| zHF0u@iAzXIsj8`KXlj|5nweWzS~We&gn?hmRgVdHU@6i$mSee*O{!I+=-ug_(sNnJGzvmo%em#fj$v%f~%hlEt@u`o~(hp2nXLNLb ztDD1t7akJptS;_Zd*{|9V?7)1;#IzjRz{i*6h&v_A16|F-F#mh{5Y z8a7V~_WtI2c+|`DrmV=?q^(!)lo{8j?Y%ZXHDR?1CzH^p2`8B3>|Wm8QGIz!tkt<^ z$Ni_hbUp3M;%?MX7qqxxuShl5jJ3kab7t8FiFiD8>2+GPf@?tn;|o^1y{sG06x}n~ z`CE7G?3|O8dp3BpGHdy(e6ULX_ZQ@taq`e$(I<)TIjK05$)9t}>rIp*;*GSv%wwZIqYtrQ1+%Igm zXbZW&+abGp@>LtfmHXChH@obrBzuZ~(oUE2+$WD2c$~Lgtr>9H*~3$L^JU%o&0kJu zyG?zPv6w5)?aq&Pa=lr#Xz4lr^RQDCj2okZ|3SN&)~v2myjWSOSm)l}ME&Ns=VgOFJ1) zYX@y4V`kA-tx~O)2m1zX(-Hv(kUUDLp<~1mLI6mCy8gc(8>s-)ABrIb7*QPqVTdA( zI9CKi%-cy=Uz)#KDJ@l3N+OZFckjt|^@Z)ouxt*9Xbcde zQh+322}miWltN)7qOj?@`rP=3M_)hWnryJQXJULj43eE=H480vk zrj}9wN~un4lv15|=$fAJ1OOOi2#^8=00@`>0&q>9DkWUk1_*q=yt=>p@WH1J&dl_7 zb$5679)IKT)co?AwS#U~zT9YlMq8~Qolb+iCIbLM2+p|>!t=Z&NdSOSs+8iKQ%X~& zYZy8BILs7b1t4EK^xWhB{hxQ=wiolbznH%Jj$0Sz7pLcz9Rr^`^Y-c+*85@T^C*g< zhMz?WI!OTlobx!2lO#!})0}gJ5T#T~$vID@Ql4AL7c2xC#u#HvHAV%GMut~+=W|v{ zzwb-;dMWMTZ=Wp|yYm@$&;DC4PR`USWj~1n$qA#i>IznX5W*NEgoF@L6h%>l6qiyG zLNrYiLP#k!ij)$Jf>NDiRpi@${|_pObNS4w-mc~4c}-`>-ag?6;YSk_sdPHq->nSN z2y42{>cKLCIHhR}R1&q4s0|P&Q7aCc6w4s=IgjEbKnM_4Q512dEXy`bn-L8oK!7MQ z^tc_@tCgvRg|p}1V^~aFdY@pN%jXBzt_|V{VUh^$FE86UdvTI}LMf#L;ZM#b z)(lhA4LhB2GFc;&CYD8PGeW>IT%Md%8kGoAmNnSd9|l2JzR=Ut!*mT(;$_mF=XG+V z6T*%WN+|+}VZsQ(o!aur8BdfFGNw2ZvU%ysIijLwt7bB~v{Yh5E98oEv-3QTip65B zzM>Gx_4LFE#~4vc1&`x64#O}A0zwD?PQnNj(RCeTYTIt1*gvpp1EWsIFiHRjripA5 z%ot;Svr%vQ4XI}5=ex4q7Zxt0EjMksQKOyKor%e*vnS8w3I!=e7>48x+A&(n-`7$q6Cf00Agxxd-3n5vl&$SQp-@;_o^7>jpp>rbRMFK#tInM~J2if#JDcCKVbh;I^ruu~rjc5ymg6{Hsja+l z_{Ev2so_4KmYm96^Pqz`^?UryL8=b zx01z$N-o=#_VOvysg&zM5cT!+ZQ8V{QmM>LUR_#Tn4h2Bv29CFv1@L6hGG&0p_CE< z!U%K0v5+cCBB4~wd8-*#%C%NAB!v9p7r)rE=f?Z*|4Lt9-{Rt8)aJcCLpiUzQH{&X ze%w}JGkN5vk38|~$1F2-@8|D6bNZB%GCkLxou0aI{v3}Jjp8^AgCMwe<0yq0RtBRK z5s(N3W5hI=!HDKqM%uN4woftGrkygq<44b&JaHjqX+pQ!Cq=r*mMt;rT zzIEGs=gy`~MifXcVEK6PL%gY}(x0(^bf% zpFjMGDiN$Ok|GK>Z&=?~ z?0)%$7hThO@V@)0rc=_fN@87O9iKX0E}t#frkgUXwCjmD*|~k20y%N@$}5Lo&ZP^t z?mw_))7bW{+fN)nTPe3}+q13ojvYI8?b^*4vs1=1PyJ?TVSeNKQQJ&Sj!)Pr$1?5N z>8VcJqLjh_QpyB`Qn9F3>k|``mo8nJo}TtR@7ZUc9qj8J8D8_^i!a`J=jY~Tm&z-R zwQJWWF^uiJaog6hAN}axO3USu;WaZ;SKm2#s!^*x^uYb5&is1y;lKQ=U1K}G`OR;B z_`wIh?*jnAgmTaomI$C$uk`eF|LtEtI)3TmXZG)V=h*q);D{@) z!ootc*>oKj0H^{<07X&T&=^CiUMa5|9^A8g`{5U#pPRXO+pW8VXx)C>-nUP@j)?-P z_U_-a_vSrRr+&Mso7(7xb@gU#VtVqXeV=~k-E(ig^+r#3-z~Qu$Yk;gwEdbjy6whH zp%(yxFd+ZoSgh<_l_-HUAg(W>1ugi)2XI0s;Sx8nU!+s(4j-ChDPtX_Y2)! zgXv6e?dZmRpT5=06{lyX7}dReR@11WNVg5$Fc@K-o|Sy}JKsYH0a9VqZnyo)%JRa( z)a|$5Vj3)$^&HQULiP^~AjB|b)k=d4lyxcjQaJa>$-@_rP87z*s=|U zP{IShS;vk}NT(s8AOYH~wqse$Oij+rZr`>0@T*6rrpJ4GiksGtuHCYI^8F7GF%&Yg z8K>2ZE?*oUpO}5|g(I7`>?*C)E?&9vwXc6oDV0P~5(Gi3r6>-Awx%08Bc`b{$^ZZp zOcUPLDYY%)Hx4|cur>PxL!iYQ#aix+-7`h5uwN|6__VL%Vwq|I^wTL0y;c8Qd%yznvE!m2tk`SZ{E0meXG@KM?uV!lx+h>jdshjtnS{PY^E!d zANWL!HX1?NHM1EHk_dq~bMnO4wo#14_N^ONtsW@$bRR$YR=Hfd@4@>VFZa9SC*MAE zGM_8%7~3{7GD47?o}PaH()sT0Zf2l%z3sQ-Fbp{$$2B7!wZc~Bx|q?FZAt*jaCK=3 zVSx!>vvy#^$m*Hd$vAAUtSr9t(xC%){ZYOHL?UC1BzH+gc%{cq_ z?|H@vj7>&Bh$eeh9#e=iX2_=#g1Hr`OFS9C_U@7&(>>rzAr=QM+Q*mk{49P>m; z1WbTPr_*p9T_FGb-S0sXW6a}rqZ!tU-GxxW+4CPRR)TbP@P;jWo`2!U(CU$tY5RUH zj9Qtj$z|JibiiV1WwFs}a8QPAwZa$?J>_|RJLUqyFiCqkjp-@NB7{gKl>*8WK{bX! z4+A+jH&-h!4-5ok z4Np(ZL@69BWL&fB_Le1XGP%vyl+Om_k4Zu`Fxz=FPQQZEf)skn2(cWxV|Kz?BE#+ z0C5~+oG@KWr_+9Ix#3qUm2mrx9a16FOy#l#!_?aCb}S;-%fw13KvF^+$2^fr0>c;) z8nhGF$=0jY{+|9g4hQ@C9mit2Wf%qq%q34^MHK+06k`U!!#HXMZG=!dovzpG!^6W- z6f?%cI2KCzt)^)^iQty)*tQD_2kmyyj-*ta3lNg%CP-yyj1on#`Ixq&yYupF8Oo+xz%L0IEwd%SqW6UrN$8mIB zCxm3OS zPy#xjM*%_r?H~w(03qb0J=d}PR@3)ef=kPC>V7Q@L(|eJ(j-WUfH6iX0|1dg9EV91 z1IR|h)F?W8=H%4$_|@@C8#auzBRFu!eH%A!B#0zD!9)Q8j6m0^m!d_Hq}okD$g~{~ z2yhmMsHtZRBWp%jg_3iI6+?&;Nt8fbsz^WrD2~Er+O`C4kmBigxX_kn$Pt?3HE2tgPjnW!X5fGdUx z!^BCM<>hjrP{?Mpw$p2yscbeoHkSR@fB)%&5BkN@(Cd@iGDx=ze`-GA({$J|VJ zK9jbs6o3SUsRY0sJ%nvL&6tUSGsY0XP8@ToDAfQXp2%1#iXo*_00B}8&J`CJ5M~%K zUYfdU8g3X$)9_wBa%8Z-x7BKG+_HN2-a9vK*$M!VgorBza1F+yYeiE*ht&cA!T_KE z1AqV^00@%b*H+m9n4Km=v`R~}Ggq6<`tRO)bKkyC#Zf!qD$_HtanlZ@r$A!b8RR8k z44^s`_7C{0{20p$00N|Q69NEq-j)iKii1$Mv!gqD!+N#R;+H2&1ASf0((Ft@F&zN~ zOi(k92_Y0?0Kgr!j$slI0);^-5C8~W3oQbPuZ{d4BPj_=0YVVA0zh2Xb-|;Rm6d!U zO$`G86i@*;jwLEJeS>L?YN#WIuQe#0?>oMf*HAWG=kVXRF`cKDAKA9v*=hQ|?Kn~< z5`-cM0Eb+HqUfsMz<>zF24Om541fS&1xf)%a2@5k=H34T)Rp>pA6@eSe2l~;fB;A& za0ya^l=6Q870>nGGR?F40000zdQ@0+LvL+uWo~o;0C?K-NX$#gO-xBu2+mI{DNig) z)lu-wOV(5H%uP&BRdCEn&PdIzR46mlGc@D^09dCESt!Wega7~l8hTV%bV^V~M=e8d zY;SUNaA9L*004N}GXekrGXOIY7Jg#@001R=R9JLMP(?>AQ(HV j004N}GpaDuF@#_M5qbjXLU~4N00000NkvXXu0mjfM6#qW literal 0 HcmV?d00001 diff --git a/lib/html/yellow_pirate.png b/lib/html/yellow_pirate.png new file mode 100644 index 0000000000000000000000000000000000000000..b6978342e3bbeabac5553aed45ab29927ca56eb8 GIT binary patch literal 612 zcmV-q0-ODbP)<&Fshlgteda3vl*+Y zYpk2OuAZ{1s)Mnizsa;Ew5GPZw@bghHNwCm#=tJSw}ZySCd9u=#>6(t$SS_F#>~nl z&de6Y#9zU_kgZR0jaXZP}L_w#xD_<;WTh;VNPF@(VM00001bW%=J06^y0W&i*H z0b)x>L;#2d9Y_EG010qNS#tmY3ljhU3ljkVnw%H_000McNliru*b5vAI49twVUhp< z0Ub$1K~xyiUD0h@f-n>Y;4`KJBdkrqkbFZWONXUQUTy#XUo{4pb??Kr=j_^ta{wA+ z)3ubB5!@N~QLjH>gT7(dUKe^M>dn5iBSY3xo4S%6Zz5zgAK8*chnvrtwCaC6)I02J zhi2Okq^)90viCo>(g-0$c>52QV=yK5wS`unh-BbMJe*FKzotVX4R9r)!F)bnYw8eC zf)Th_#q0I1r7AjhFkNwezTtdDu>mHb%aagkONbMvn1M$q7g<(xGQk7v05HnaqO3Y) z#wiH_Li#3(>iXTdmg$Z}b*rMyIW4=mD$W~Ux7^#tNt&zoYL)pV4#KA4BR@_qxpqn7 ySEn1#gB7TPl6>sg^W89Q*B?$MkBgbz+. +# +# == Change history +# Version 1.0:: 11 Jun 2008 +# * Initial build +# 1.1:: 5 Dec 2008 +# => * Now tracks played cards when generating new games + +# Symbols +$SYMBOLS = [:bottle, :dagger, :gun, :hat, :keys, :skull] + +# The number of cards of each symbol in the deck +$CARDS_PER_SYMBOL = 17 + +# The number of cards initiall dealt to each player +$INITIAL_CARDS_PER_PLAYER = 3 + +# Maximum number of pieces on a position +$MAX_PIECES_PER_POSITION = 3 + +# Number of actions that can be taken by each player in sequence +$MAX_MOVES_PER_TURN = 3 + +# Errors for a game + +# Moves can only be [1..6] spaces +class InvalidMoveError < StandardError +end + +# Game is won when only one player has uncaptured pieces +class GameWonNotice < StandardError +end + +# A position on the board. +class Position + attr_accessor :symbol + attr_accessor :contains + + def initialize(symbol) + @symbol = symbol + @contains = [] + end +end + +# A tile that makes up the board. Each tile has two sides, each with six +# positions. Tiles can be either way up, and either way round. +class Tile + attr_reader :exposed, :front, :back + + def initialize(front, back) + @front = front.collect {|s| Position.new(s)} + @back = back.collect {|s| Position.new(s)} + @exposed = @front + @exposed_side = :front + end + + def flip! + if @exposed_side == :front + @exposed = @back + @exposed_side = :back + else + @exposed = @front + @exposed_side = :front + end + end + + def reverse! + @exposed = @exposed.reverse + end +end + + +# The game board +class Board + + attr_accessor :positions + attr_reader :tiles + + # A laborious procedure to create all the positions and tie them all together + def initialize(tiles_used) + # A hash of all positions, indexed by position names + @tiles = [Tile.new([:hat, :keys, :gun, :bottle, :skull, :dagger], + [:hat, :keys, :gun, :bottle, :skull, :dagger]), + Tile.new([:gun, :hat, :dagger, :skull, :bottle, :keys], + [:gun, :hat, :dagger, :skull, :bottle, :keys]), + Tile.new([:skull, :gun, :bottle, :keys, :dagger, :hat], + [:skull, :gun, :bottle, :keys, :dagger, :hat]), + Tile.new([:dagger, :bottle, :keys, :gun, :hat, :skull], + [:dagger, :bottle, :keys, :gun, :hat, :skull]), + Tile.new([:keys, :dagger, :skull, :hat, :gun, :bottle], + [:keys, :dagger, :skull, :hat, :gun, :bottle]), + Tile.new([:bottle, :skull, :hat, :dagger, :keys, :gun], + [:bottle, :skull, :hat, :dagger, :keys, :gun]) + ].shuffle[0...tiles_used] + @tiles = @tiles.each do |t| + if rand < 0.5 + t.reverse! + else + t + end + end + + @positions = [Position.new(:cell)] + @tiles.each {|t| t.exposed.each {|p| @positions << p}} + @positions << Position.new(:boat) + end # def + + def to_s + layout + end + + def to_str + to_s + end + + + # For each position, show its name and what it touches + def layout + out_string = "" + @positions.each {|position| out_string << "#{position.symbol}\n"} + out_string + end + +end + + +# Each piece on the board is an object +class Piece + attr_reader :player, :number + attr_accessor :position + + def initialize(position, player, number) + @position = position + @player = player + @number = number + end + + def to_s + "#{@player}:#{@number}" + end + + def to_str + to_s + end + + def show(convert_to_zero_based_player_number = false) + if convert_to_zero_based_player_number + "#{@player + 1}:#{@number}" + else + "#{@player}:#{@number}" + end + end + + def move_to(new_position) + @position = new_position + end + +end + + +# A move in a game +class Move + attr_reader :piece, :origin, :destination, :card_played + + def initialize(piece, origin, destination, card_played = :unspecified) + @piece = piece + @origin = origin + @destination = destination + @card_played = card_played + end + + def show(board, convert_to_zero_based_player_number = false) + if @card_played == :unspecified + "#{@piece.show(convert_to_zero_based_player_number)}: #{board.positions.index(@origin)} -> #{board.positions.index(@destination)}" + else + "#{@piece.show(convert_to_zero_based_player_number)}: #{board.positions.index(@origin)} -> #{board.positions.index(@destination)} (#{@card_played})" + end + end + + def format(board, convert_to_zero_based_player_number = false) + display_player_number = if convert_to_zero_based_player_number then + @piece.player + 1 + else + @piece.player + end + if @card_played ==:unspecified + "#{display_player_number} #{board.positions.index(@origin)} #{board.positions.index(@destination)}" + else + "#{display_player_number} #{board.positions.index(@origin)} #{board.positions.index(@destination)} #{@card_played}" + end + end + + # Write a move to a string + # Note the inverse, String#to_move, is defined below + def to_s + "#{@piece.player} #{@origin.to_s} #{@destination.to_s} #{@card_played}" + end + + def to_str + to_s + end + +end + + + +# A class to record each of the states previously found in a game. +# Note that this is a deep copy of the pieces and what they've captured, so +# you'll need to convert back +class GameState + attr_accessor :move, :player, :board, :pieces_before_move, :deck_before_move, + :players_cards_before_move, :moves_by_current_player + # this_game_state = GameState.new(move, player, @board, @pieces, @deck, @players_cards) + + + def initialize(move, player, board, pieces, deck, players_cards, moves_by_current_player) + @move, @player, @board, @pieces_before_move, @deck_before_move, + @players_cards_before_move, @moves_by_current_player = + copy_game_state(move, player, board, pieces, deck, players_cards, + moves_by_current_player) + end + + # def ==(other) + # @move.to_s == other.move.to_s and + # @player == other.player and + # @piece_after_move == other.pieces_after_move + # end + + def copy_game_state(move, player, board, pieces, deck, players_cards, moves_by_current_player) + copy_player = player + moving_player = move.piece.player + + copy_board = board.dup + copy_board.positions = board.positions.collect {|pos| pos.dup} + copy_board.positions.each {|pos| pos.contains = []} + + copy_pieces = pieces.collect do |players_pieces| + players_pieces.collect do |piece| + new_piece_position = copy_board.positions[board.positions.index(piece.position)] + new_piece = Piece.new(new_piece_position, piece.player, piece.number) + new_piece_position.contains << new_piece + new_piece + end + end + + piece_index = pieces[moving_player].index(move.piece) + origin_index = board.positions.index(move.origin) + destination_index = board.positions.index(move.destination) + copy_move = Move.new(copy_pieces[moving_player][piece_index], + copy_board.positions[origin_index], + copy_board.positions[destination_index]) + + copy_deck = deck.dup + copy_players_cards = players_cards.collect {|p| p.dup} + copy_moves_by_current_player = moves_by_current_player + + return copy_move, copy_player, copy_board, copy_pieces, copy_deck, copy_players_cards, copy_moves_by_current_player + end + +end + + +# A game of Cartagena. It keeps a history of all previous states. +class Game + + attr_reader :history + attr_accessor :current_player + attr_reader :players + attr_accessor :players_cards + attr_accessor :moves_by_current_player + attr_reader :board + attr_reader :pieces + attr_reader :cards + attr_accessor :deck + + # Create a new game + def initialize(players = 5, number_of_tiles = 6, pieces_each = 6) + @board = Board.new(number_of_tiles) + @history = [] + @pieces = [] + @players = [].fill(0, players) {|i| i} + @players_cards = [] + @current_player = 0 + @moves_by_current_player = 0 + @cards = [] + 1.upto($CARDS_PER_SYMBOL) {|x| @cards.concat($SYMBOLS)} + @deck = @cards.shuffle + @players.each do |p| + @pieces[p] = [] + 0.upto(pieces_each - 1) do |count| + piece = Piece.new(@board.positions[0], p, count) + @pieces[p][count] = piece + @board.positions[0].contains << piece + end + @players_cards[p] = [] + deal_cards!($INITIAL_CARDS_PER_PLAYER, p) + end + end + + # Deal some cards to a player. Remove them from the deck. Refill the deck + # if necessary + def deal_cards!(number_of_cards, player = @current_player) + 1.upto(number_of_cards) do + if @deck.empty? + @deck = @cards + @players_cards.each do |p| + p.each do |c| + @deck.delete_at(@deck.index(c)) + end + end + @deck = @deck.shuffle + end + @players_cards[player] << @deck.pop + end + end + + # Check that a move is valid. Throw an exception if it's invalid + def validate_move(move, player = @current_player) + # Check the move is a valid one + raise(InvalidMoveError, "Move #{move}: Player #{player} does not exist") unless @players.include?(player) + raise(InvalidMoveError, "Move #{move}: None of player #{player}'s pieces on position #{move.origin}") unless move.origin.contains.find {|pc| pc.player == player} + raise(InvalidMoveError, "Move #{move}: Origin and destination are the same") if move.origin == move.destination + + origin_position = @board.positions.index(move.origin) + destination_position = @board.positions.index(move.destination) + # Is this move an advance or a retreat? + if destination_position > origin_position + # Advancing a piece + if move.destination == @board.positions[-1] # A move into the boat + raise(InvalidMoveError, "Move #{move}: Move into boat and card unspecified") if move.destination == @board.positions[-1] and move.card_played == :unspecified + unless @players_cards[player].find {|c| c == move.card_played} + raise(InvalidMoveError, "Player #{player} does not have a card to move a piece into the boat") + end + else + if move.card_played != :unspecified and move.destination.symbol != move.card_played + raise(InvalidMoveError, "Player #{player} trying to move to #{move.destination}, a #{move.destination.symbol} square with a a #{move.card_played} card") + end + unless @players_cards[player].find {|c| c == move.destination.symbol} + raise(InvalidMoveError, "Player #{player} does not have a card to move a piece to a #{move.destination.symbol} square") + end + # Check target square is vacant + raise(InvalidMoveError, "Advance move #{move}: destination occupied") unless move.destination.contains.empty? + end + # Check all the intervening squares with this symbol are occupied + intervening_empty_position = @board.positions[origin_position...destination_position].index_find do |p| + p.symbol == move.destination.symbol and + p.contains.empty? + end + raise(InvalidMoveError, "Advance move #{move}: location #{intervening_empty_position} is empty") if intervening_empty_position + else + # Retreating a piece + # Check target position has one or two pieces already on it + destination_count = move.destination.contains.length + raise(InvalidMoveError, "Retreat move #{move}: destination has no pieces already on it") if destination_count == 0 + raise(InvalidMoveError, "Retreat move #{move}: destination has too many (#{destination_count}) pieces already on it") if destination_count >= $MAX_PIECES_PER_POSITION + # Check none of the intervening squares have any pieces on them + # puts "Checking positions #{destination_position} to #{origin_position}" + intervening_target_position = @board.positions[(destination_position + 1)...origin_position].index_find do |p| + # puts "Examining postition #{p} at location #{@board.positions.index(p)} which contains #{p.contains.length} pieces" + p.contains.length > 0 and + p.contains.length < $MAX_PIECES_PER_POSITION + end + raise(InvalidMoveError, "Retreat move #{move.show(@board)}: location #{intervening_target_position} is a viable target") if intervening_target_position + end + end + + # Apply a single move to a game. + def apply_move!(move, player = @current_player, validate = true) + + validate_move(move, player) if validate + + raise(InvalidMoveError, "Too many consecutive moves by #{@current_player}: has taken #{@moves_by_current_player} and validate is #{validate}") if validate and player == @current_player and @moves_by_current_player >= $MAX_MOVES_PER_TURN + + # Record the old state + this_game_state = GameState.new(move, @current_player, @board, @pieces, @deck, @players_cards, @moves_by_current_player) + @history << this_game_state + + # Apply this move + move.origin.contains.delete move.piece + move.destination.contains << move.piece + move.piece.position = move.destination + + if player == @current_player + @moves_by_current_player += 1 + else + @current_player = player + @moves_by_current_player = 1 + end + + # Update cards + if @board.positions.index(move.destination) > @board.positions.index(move.origin) + # Advance move + if validate + card_to_remove = if move.card_played != :unspecified + move.card_played + else + move.destination.symbol + end + if @players_cards[player].include?(card_to_remove) + @players_cards[player].delete_at(@players_cards[player].index(card_to_remove)) + end + end + else + # Retreat move + deal_cards!(move.destination.contains.length - 1, player) + end + + # If this player has all their pieces in the boat, declare a win. + if @pieces[player].all? {|pc| pc.position == @board.positions[-1]} + raise(GameWonNotice, "Game won by #{player}") + end + end + + # Undo a move + def undo_move! + state_to_restore = @history[-1] + move, @current_player, @board, @pieces, @deck, @players_cards, + @moves_by_current_player = + state_to_restore.copy_game_state(state_to_restore.move, + state_to_restore.player, + state_to_restore.board, + state_to_restore.pieces_before_move, + state_to_restore.deck_before_move, + state_to_restore.players_cards_before_move, + state_to_restore.moves_by_current_player) + @history.pop + end + + # Apply a list of moves in order + def apply_moves!(moves) + moves.each do |move| + @current_player = move.piece.player + apply_move!(move, @current_player) + next_player! if @moves_by_current_player >= $MOVES_PER_TURN + end + end + + + # Set the current player to be the next player + def next_player! + if @current_player == @players[-1] + @current_player = @players[0] + else + @current_player = @players[@players.index(@current_player) + 1] + end + @moves_by_current_player = 0 + @current_player + end + + + def reset_current_player(new_current_player) + @current_player = new_current_player + @moves_by_current_player = 0 + end + + + # Return an array of all possible moves from this state, given the active player + def possible_moves(player = @current_player) + moves = [] + @pieces[player].each do |piece| + # Do a forward move for each card held + unless piece.position == @board.positions[-1] + @players_cards[player].each do |card| + destination = @board.positions[@board.positions.index(piece.position)..-1].find do |pos| + (pos.symbol == card and pos.contains == []) or + pos.symbol == :boat + end + # puts "Player #{player}, card #{card}, piece #{piece.number} at position #{@board.positions.index(piece.position)} to #{@board.positions.index(destination)}, a #{destination.symbol}" + moves << Move.new(piece, piece.position, destination, card) + end + end + # Do a reverse move for the piece + unless piece.position == board.positions[0] + destination = @board.positions[1...@board.positions.index(piece.position)].reverse.find do |pos| + pos.contains.length == 1 or pos.contains.length == 2 + end + if destination + # puts "Player #{player}, piece #{piece.number} at position #{@board.positions.index(piece.position)} retreats to #{@board.positions.index(destination)}, a #{destination.symbol} containing #{destination.contains.length} pieces" + moves << Move.new(piece, piece.position, destination) + end + end + end + # moves.each {|m| puts m.show(@board)} + moves + end + + def build_state_string + outstr = "Current player = #{@current_player}\n" + 0.upto((@board.positions.length)-1) do |i| + outstr << "#{i}: #{@board.positions[i].symbol}: " + @board.positions[i].contains.each do |piece| + outstr << "P#{piece.player}:#{piece.number} " + end + outstr << "\n" + end + 0.upto((@players.length)-1) do |i| + outstr << "Player #{i} holds " << (@players_cards[i].sort_by {|c| c.to_s}).join(', ') << "\n" + end + outstr << "Deck holds " << @deck.join(', ') << "\n" + outstr + end + + # Show the state of the board + def show_state + puts build_state_string + end + + def to_s + show_state + end + + def to_str + to_s + end + + + def set_testing_game! + srand 1234 + @board = nil + initialize(5, 6, 6) + board_symbols = [:cell, :gun, :keys, :dagger, :hat, :skull, :bottle, + :keys, :dagger, :skull, :hat, :gun, :bottle, + :dagger, :bottle, :keys, :gun, :hat, :skull, + :dagger, :skull, :bottle, :gun, :keys, :hat, + :hat, :dagger, :keys, :bottle, :gun, :skull, + :keys, :bottle, :skull, :dagger, :hat, :gun, :boat] + board_symbols.each_index do |i| + @board.positions[i].symbol = board_symbols[i] + end + + @players_cards[0] = [:gun, :hat, :skull] + @players_cards[1] = [:bottle, :keys, :keys] + @players_cards[2] = [:bottle, :hat, :keys] + @players_cards[3] = [:bottle, :skull, :skull] + @players_cards[4] = [:bottle, :gun, :gun] + + @deck = [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle, :skull] + + end + + # Given a set of lines from an input file, turn them into a Game object and + # a set of Move objects. + # Note the multiple return values. + # Class method + def Game.read_game(gamelines) + gamelines.each {|l| l.chomp!} + game = Game.new(gamelines[0].to_i, 6, 6) + + # create the board + 2.upto(38) do |i| + game.board.positions[i-1] = Position.new(gamelines[i].to_sym) + end + + # read all the moves so far + moves = [] + i = 39 + current_player_moves = 0 + current_player = 0 + player_card_counts = game.players_cards.map {|p| $INITIAL_CARDS_PER_PLAYER} + unused_cards = game.cards.map {|c| c} + while gamelines[i].in_move_format? + move = gamelines[i].to_move(game, true) + if move.piece.player == current_player + current_player_moves += 1 + else + current_player = move.piece.player + current_player_moves = 1 + end + # if move is an advance move, decrement the cards held by the player and remove the card from unused cards + # throw an error if an advance move is made when all the cards of that type have been used + # if unused_cards becomes empty, restock it. + if game.board.positions.index(move.destination) > game.board.positions.index(move.origin) + raise(InvalidMoveError, "Game reading: Player #{move.piece.player} attempting an advance move with no cards in hand. Move is #{gamelines[i]} on line #{i}") if player_card_counts[move.piece.player] == 0 + card_used = move.destination.symbol + if card_used == :boat + card_used = move.card_played + end + raise(InvalidMoveError, "Attempting to move into boat without a card specified") if card_used == :unspecified + raise(InvalidMoveError, "Game reading: Player #{move.piece.player} attempting an advance move to a #{move.destination.symbol} place when all those cards have been used. Move is #{gamelines[i]} on line #{i}") unless unused_cards.include? card_used + player_card_counts[current_player] -= 1 + unused_cards.delete_at(unused_cards.index(card_used)) + # if move is a retreat move, increment the cards held by the player + else + player_card_counts[current_player] += move.destination.contains.length + if unused_cards.length == player_card_counts.inject {|sum, n| sum + n } + unused_cards = game.cards.map {|c| c} + end + end + game.apply_move!(move, move.piece.player, false) + moves << move + i = i + 1 + end + + # note the current player + game.current_player = gamelines[i].to_i - 1 + if game.current_player == current_player + game.moves_by_current_player = current_player_moves + else + game.moves_by_current_player = 0 + end + current_player = game.current_player + + # read the cards + game.players_cards = game.players_cards.map {|c| []} + (i+1).upto(gamelines.length - 1) do |j| + game.players_cards[game.current_player] << gamelines[j].to_sym + raise(InvalidMoveError, "Player #{game.current_player} given a #{gamelines[j]} card, but none left in deck") unless unused_cards.index(gamelines[j].to_sym) + unused_cards.delete_at(unused_cards.index(gamelines[j].to_sym)) + end + raise(InvalidMoveError, "Player #{game.current_player} given #{game.players_cards[game.current_player].length} cards, but should have #{player_card_counts[game.current_player]} cards from play") if game.players_cards[game.current_player].length != player_card_counts[game.current_player] + + # Update the game deck + game.deck = unused_cards.shuffle + player_card_counts[game.current_player] = 0 + player_card_counts.each_index do |player| + if player_card_counts[player] > 0 + game.deal_cards!(player_card_counts[player], player) + end + end + return game, moves + end + +end + + +# Extension to String class to convert a move-description string into a Move object. +# This is the inverse of the Move#to_s method +class String + def in_move_format? + elements = split + return ((elements.length == 3 or elements.length == 4) and + elements[0].to_i > 0 and elements[0].to_i < 6 and + elements[1].to_i >= 0 and elements[1].to_i <= 37 and + elements[2].to_i >= 0 and elements[2].to_i <= 37) + end + + def to_move(game, convert_to_zero_based_player_number = false) + move_elements = self.downcase.split + player = move_elements[0].to_i + player = player - 1 if convert_to_zero_based_player_number + origin_index = move_elements[1].to_i + destination_index = move_elements[2].to_i + raise(InvalidMoveError, "Invalid origin #{origin_index} in move read") unless origin_index >= 0 and origin_index < game.board.positions.length + raise(InvalidMoveError, "Invalid destination #{destination_index} in move read") unless destination_index > 0 and destination_index <= game.board.positions.length + raise(InvalidMoveError, "Player #{player} does not have a piece on position #{origin_index} in move read") unless game.board.positions[origin_index].contains.any? {|p| p.player == player} + piece = game.board.positions[origin_index].contains.find {|p| p.player == player} + piece_name = piece.number + if destination_index > origin_index + if move_elements.length == 4 + card_used = move_elements[3].chomp.to_sym + raise(InvalidMoveError, "Card used (#{card_used}) does not match destination space (#{game.board.positions[destination_index].symbol})") unless card_used == game.board.positions[destination_index].symbol or destination_index == game.board.positions.length - 1 + Move.new(game.pieces[player][piece_name], + game.board.positions[origin_index], + game.board.positions[destination_index], + card_used) + else + card_used = :unspecified + raise(InvalidMoveError, "Move to boat without specifying card used") if destination_index == game.board.positions.length + 1 + Move.new(game.pieces[player][piece_name], + game.board.positions[origin_index], + game.board.positions[destination_index]) + end + else + Move.new(game.pieces[player][piece_name], + game.board.positions[origin_index], + game.board.positions[destination_index]) + end + end +end + + +# Read a game description file and convert it into a Game object and set of Move objects. +# Note that Game.read_game method returns multiple values, so this one does too. +class IO + def IO.read_game(filename) + gamelines = IO.readlines(filename) + return Game.read_game(gamelines) + end +end + +# Extension to the Array class to include the Array#shuffle function +class Array + def shuffle + sort_by { rand } + end + + def index_find(&block) + found = find(&block) + if found + index found + else + found + end + end +end diff --git a/lib/main.rb b/lib/main.rb new file mode 100644 index 0000000..bf9b84d --- /dev/null +++ b/lib/main.rb @@ -0,0 +1,6 @@ +# +# To change this template, choose Tools | Templates +# and open the template in the editor. + + +puts "Hello World" diff --git a/lib/random-move.rb b/lib/random-move.rb new file mode 100755 index 0000000..53f6c5f --- /dev/null +++ b/lib/random-move.rb @@ -0,0 +1,27 @@ +#!/usr/bin/ruby +# +# == Synopsis +# +# Play one move of a Cartagena game +# +# == Usage +# clockwise-p1 +# Game state file read on STDIN +# Move produced on STDOUT +# +# == Author +# Neil Smith + +# +# To change this template, choose Tools | Templates +# and open the template in the editor. + + +require 'libcartagena' +require 'rdoc/usage' + +gamelines = readlines +game, moves = Game.read_game(gamelines) +possibles = game.possible_moves +move = possibles[rand(possibles.length)] +puts move.format(game.board, true) diff --git a/nbproject/.svn/all-wcprops b/nbproject/.svn/all-wcprops new file mode 100644 index 0000000..13843cb --- /dev/null +++ b/nbproject/.svn/all-wcprops @@ -0,0 +1,17 @@ +K 25 +svn:wc:ra_dav:version-url +V 47 +/svn/njae/!svn/ver/33/cartagena/trunk/nbproject +END +project.properties +K 25 +svn:wc:ra_dav:version-url +V 66 +/svn/njae/!svn/ver/25/cartagena/trunk/nbproject/project.properties +END +project.xml +K 25 +svn:wc:ra_dav:version-url +V 59 +/svn/njae/!svn/ver/25/cartagena/trunk/nbproject/project.xml +END diff --git a/nbproject/.svn/dir-prop-base b/nbproject/.svn/dir-prop-base new file mode 100644 index 0000000..a7ce626 --- /dev/null +++ b/nbproject/.svn/dir-prop-base @@ -0,0 +1,6 @@ +K 10 +svn:ignore +V 8 +private + +END diff --git a/nbproject/.svn/entries b/nbproject/.svn/entries new file mode 100644 index 0000000..82adaef --- /dev/null +++ b/nbproject/.svn/entries @@ -0,0 +1,52 @@ +8 + +dir +65 +http://scripts.njae.me.uk/svn/njae/cartagena/trunk/nbproject +http://scripts.njae.me.uk/svn/njae + + + +2008-09-11T15:29:31.198093Z +33 +neil +has-props + +svn:special svn:externals svn:needs-lock + + + + + + + + + + + +0ada9808-85ab-4924-adda-5bf89eae3818 + +project.properties +file + + + + +2008-08-19T15:48:21.000000Z +35e60cf760ae7ae20c4dbd368bbaa70c +2008-06-12T10:24:34.892518Z +25 +neil + +project.xml +file + + + + +2008-08-19T15:48:21.000000Z +7d61aa960131423aec0855b658116b4a +2008-06-12T10:24:34.892518Z +25 +neil + diff --git a/nbproject/.svn/format b/nbproject/.svn/format new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/nbproject/.svn/format @@ -0,0 +1 @@ +8 diff --git a/nbproject/.svn/text-base/project.properties.svn-base b/nbproject/.svn/text-base/project.properties.svn-base new file mode 100644 index 0000000..afee2e6 --- /dev/null +++ b/nbproject/.svn/text-base/project.properties.svn-base @@ -0,0 +1,4 @@ +main.file=main.rb +source.encoding=UTF-8 +src.dir=lib +test.src.dir=test diff --git a/nbproject/.svn/text-base/project.xml.svn-base b/nbproject/.svn/text-base/project.xml.svn-base new file mode 100644 index 0000000..4646cb9 --- /dev/null +++ b/nbproject/.svn/text-base/project.xml.svn-base @@ -0,0 +1,15 @@ + + + org.netbeans.modules.ruby.rubyproject + + + Cartagena + + + + + + + + + diff --git a/nbproject/private/private.xml b/nbproject/private/private.xml new file mode 100644 index 0000000..b27cee1 --- /dev/null +++ b/nbproject/private/private.xml @@ -0,0 +1,7 @@ + + + + + file:/home/neil/programming/ruby/Cartagena/lib/cgi/cartagena-simple.rb + + diff --git a/nbproject/private/rake-d.txt b/nbproject/private/rake-d.txt new file mode 100644 index 0000000..e69de29 diff --git a/nbproject/project.properties b/nbproject/project.properties new file mode 100644 index 0000000..afee2e6 --- /dev/null +++ b/nbproject/project.properties @@ -0,0 +1,4 @@ +main.file=main.rb +source.encoding=UTF-8 +src.dir=lib +test.src.dir=test diff --git a/nbproject/project.xml b/nbproject/project.xml new file mode 100644 index 0000000..4646cb9 --- /dev/null +++ b/nbproject/project.xml @@ -0,0 +1,15 @@ + + + org.netbeans.modules.ruby.rubyproject + + + Cartagena + + + + + + + + + diff --git a/test-game-play b/test-game-play new file mode 100644 index 0000000..c3998b1 --- /dev/null +++ b/test-game-play @@ -0,0 +1,135 @@ +5 +cell +gun +keys +dagger +hat +skull +bottle +keys +dagger +skull +hat +gun +bottle +dagger +bottle +keys +gun +hat +skull +dagger +skull +bottle +gun +keys +hat +hat +dagger +keys +bottle +gun +skull +keys +bottle +skull +dagger +hat +gun +boat +1 0 5 +1 0 4 +1 5 4 +2 0 2 +2 0 7 +2 7 4 +3 0 7 +3 0 10 +3 0 6 +4 0 12 +4 12 10 +4 0 5 +5 0 12 +5 12 10 +5 0 3 +1 0 9 +1 0 1 +1 9 7 +2 0 12 +2 0 14 +2 12 7 +3 6 5 +3 0 11 +3 11 5 +4 0 9 +4 0 18 +4 18 14 +5 0 11 +5 0 16 +5 16 14 +1 0 15 +1 15 11 +1 11 9 +2 0 15 +2 0 17 +2 17 15 +3 0 23 +3 23 15 +3 0 17 +4 10 9 +4 0 24 +4 0 8 +5 0 16 +5 16 11 +5 11 10 +1 0 18 +1 7 12 +1 12 11 +2 14 11 +2 2 23 +2 15 14 +3 17 15 +3 5 27 +3 5 13 +4 8 7 +4 5 17 +4 7 12 +5 0 8 +5 8 7 +5 3 31 +1 18 17 +1 1 16 +1 17 16 +2 4 37 keys +2 7 4 +2 4 37 keys +3 7 21 +3 13 12 +3 10 13 +4 17 16 +4 9 28 +4 9 18 +5 7 37 keys +5 11 10 +5 10 32 +1 11 10 +1 4 8 +1 4 17 +2 11 37 keys +2 14 37 keys +2 15 37 keys +3 13 12 +3 12 22 +3 12 25 +4 16 15 +4 12 35 +4 14 29 +5 10 20 +5 10 30 +5 14 36 +1 17 16 +1 8 33 skull +1 9 17 hat +2 +dagger +hat \ No newline at end of file diff --git a/test-game-play-to-win b/test-game-play-to-win new file mode 100644 index 0000000..e5b864f --- /dev/null +++ b/test-game-play-to-win @@ -0,0 +1,135 @@ +5 +cell +gun +keys +dagger +hat +skull +bottle +keys +dagger +skull +hat +gun +bottle +dagger +bottle +keys +gun +hat +skull +dagger +skull +bottle +gun +keys +hat +hat +dagger +keys +bottle +gun +skull +keys +bottle +skull +dagger +hat +gun +boat +1 0 5 +1 0 4 +1 5 4 +2 0 2 +2 0 7 +2 7 4 +3 0 7 +3 0 10 +3 0 6 +4 0 12 +4 12 10 +4 0 5 +5 0 12 +5 12 10 +5 0 3 +1 0 9 +1 0 1 +1 9 7 +2 0 12 +2 0 14 +2 12 7 +3 6 5 +3 0 11 +3 11 5 +4 0 9 +4 0 18 +4 18 14 +5 0 11 +5 0 16 +5 16 14 +1 0 15 +1 15 11 +1 11 9 +2 0 15 +2 0 17 +2 17 15 +3 0 23 +3 23 15 +3 0 17 +4 10 9 +4 0 24 +4 0 8 +5 0 16 +5 16 11 +5 11 10 +1 0 18 +1 7 12 +1 12 11 +2 14 11 +2 2 23 +2 15 14 +3 17 15 +3 5 27 +3 5 13 +4 8 7 +4 5 17 +4 7 12 +5 0 8 +5 8 7 +5 3 31 +1 18 17 +1 1 16 +1 17 16 +2 4 37 keys +2 7 4 +2 4 37 keys +3 7 21 +3 13 12 +3 10 13 +4 17 16 +4 9 28 +4 9 18 +5 7 37 keys +5 11 10 +5 10 32 +1 11 10 +1 4 8 +1 4 17 +2 11 37 keys +2 14 37 keys +2 15 37 keys +3 13 12 +3 12 22 +3 12 25 +4 16 15 +4 12 35 +4 14 29 +5 10 20 +5 10 30 +5 14 36 +1 17 16 +1 8 33 skull +1 9 17 hat +2 23 37 hat +2 +dagger \ No newline at end of file diff --git a/test-load-file-1 b/test-load-file-1 new file mode 100644 index 0000000..5fc420f --- /dev/null +++ b/test-load-file-1 @@ -0,0 +1,53 @@ +3 +cell +skull +hat +keys +gun +bottle +dagger +hat +gun +keys +skull +bottle +dagger +dagger +gun +hat +bottle +skull +keys +hat +keys +bottle +gun +skull +keys +keys +skull +hat +bottle +gun +dagger +bottle +skull +gun +dagger +keys +hat +boat +1 0 4 +1 0 3 +1 4 3 +2 0 4 +2 4 3 +2 0 2 +3 0 5 +3 5 2 +3 0 4 +1 0 5 +2 +hat +skull +hat \ No newline at end of file diff --git a/test-load-file-2 b/test-load-file-2 new file mode 100644 index 0000000..51654c6 --- /dev/null +++ b/test-load-file-2 @@ -0,0 +1,43 @@ +3 +cell +skull +hat +keys +gun +bottle +dagger +hat +gun +keys +skull +bottle +dagger +dagger +gun +hat +bottle +skull +keys +hat +keys +bottle +gun +skull +keys +keys +skull +hat +bottle +gun +dagger +bottle +skull +gun +dagger +keys +hat +boat +1 +hat +skull +hat \ No newline at end of file diff --git a/test-load-file-3 b/test-load-file-3 new file mode 100644 index 0000000..8835d12 --- /dev/null +++ b/test-load-file-3 @@ -0,0 +1,43 @@ +5 +cell +skull +hat +keys +gun +bottle +dagger +hat +gun +keys +skull +bottle +dagger +dagger +gun +hat +bottle +skull +keys +hat +keys +bottle +gun +skull +keys +keys +skull +hat +bottle +gun +dagger +bottle +skull +gun +dagger +keys +hat +boat +1 0 4 +1 0 5 +1 +hat \ No newline at end of file diff --git a/test/.svn/all-wcprops b/test/.svn/all-wcprops new file mode 100644 index 0000000..2c02335 --- /dev/null +++ b/test/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 42 +/svn/njae/!svn/ver/56/cartagena/trunk/test +END +libcartagena_test.rb +K 25 +svn:wc:ra_dav:version-url +V 63 +/svn/njae/!svn/ver/56/cartagena/trunk/test/libcartagena_test.rb +END diff --git a/test/.svn/entries b/test/.svn/entries new file mode 100644 index 0000000..7a70690 --- /dev/null +++ b/test/.svn/entries @@ -0,0 +1,39 @@ +8 + +dir +65 +http://scripts.njae.me.uk/svn/njae/cartagena/trunk/test +http://scripts.njae.me.uk/svn/njae + + + +2008-12-05T21:50:45.570571Z +56 +neil + + +svn:special svn:externals svn:needs-lock + + + + + + + + + + + +0ada9808-85ab-4924-adda-5bf89eae3818 + +libcartagena_test.rb +file + + + + +2008-12-21T01:02:45.000000Z +bc3d0bf47514e1c2583326dda72433f3 +2008-12-05T21:50:45.570571Z +56 + diff --git a/test/.svn/format b/test/.svn/format new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/test/.svn/format @@ -0,0 +1 @@ +8 diff --git a/test/.svn/text-base/libcartagena_test.rb.netbeans-base b/test/.svn/text-base/libcartagena_test.rb.netbeans-base new file mode 100644 index 0000000..1cab850 --- /dev/null +++ b/test/.svn/text-base/libcartagena_test.rb.netbeans-base @@ -0,0 +1,2065 @@ +# +# To change this template, choose Tools | Templates +# and open the template in the editor. + + +$:.unshift File.join(File.dirname(__FILE__),'..','lib', 'libcartagena') + +require 'test/unit' +require File.join(File.dirname(__FILE__), '..', 'lib', 'libcartagena') + +class TileTest < Test::Unit::TestCase + def test_tile_init + front = [:hat, :keys, :gun, :bottle, :skull, :dagger] + back = [:gun, :hat, :dagger, :skull, :bottle, :keys] + assert_nothing_raised { Tile.new(front, back) } + tile = Tile.new(front, back) + + assert_equal 6, tile.front.length + assert_equal 6, tile.back.length + assert_equal 6, tile.exposed.length + assert_equal front, tile.front.map {|p| p.symbol} + assert_equal back, tile.back.map {|p| p.symbol} + assert_equal front, tile.exposed.map {|p| p.symbol} + + tile.flip! + assert_equal 6, tile.front.length + assert_equal 6, tile.back.length + assert_equal 6, tile.exposed.length + assert_equal front, tile.front.map {|p| p.symbol} + assert_equal back, tile.back.map {|p| p.symbol} + assert_equal back, tile.exposed.map {|p| p.symbol} + + tile.flip! + assert_equal 6, tile.front.length + assert_equal 6, tile.back.length + assert_equal 6, tile.exposed.length + assert_equal front, tile.front.map {|p| p.symbol} + assert_equal back, tile.back.map {|p| p.symbol} + assert_equal front, tile.exposed.map {|p| p.symbol} + + tile.flip! + tile.reverse! + assert_equal 6, tile.front.length + assert_equal 6, tile.back.length + assert_equal 6, tile.exposed.length + assert_equal front, tile.front.map {|p| p.symbol} + assert_equal back, tile.back.map {|p| p.symbol} + assert_equal back.reverse, tile.exposed.map {|p| p.symbol} + + tile.flip! + assert_equal 6, tile.front.length + assert_equal 6, tile.back.length + assert_equal 6, tile.exposed.length + assert_equal front, tile.front.map {|p| p.symbol} + assert_equal back, tile.back.map {|p| p.symbol} + assert_equal front, tile.exposed.map {|p| p.symbol} + + tile.reverse! + assert_equal 6, tile.front.length + assert_equal 6, tile.back.length + assert_equal 6, tile.exposed.length + assert_equal front, tile.front.map {|p| p.symbol} + assert_equal back, tile.back.map {|p| p.symbol} + assert_equal front.reverse, tile.exposed.map {|p| p.symbol} + + end +end + +class BoardTest < Test::Unit::TestCase + def test_board_init + assert_nothing_raised { Board.new(6) } + board = Board.new(1) + assert_equal 1, board.tiles.length + assert_equal 8, board.positions.length + + board = Board.new(6) + assert_equal 6, board.tiles.length + assert_equal 38, board.positions.length + + 0.upto(5) do |i| + start = i * 6 + 1 + slice = board.positions[start..(start+5)] + $SYMBOLS.each do |tile| + assert_equal 1, (slice.find_all {|p| p.symbol == tile}).length + end + end + end +end + + +class GameTest < Test::Unit::TestCase + def test_game_init + assert_nothing_raised { Game.new } + + game = Game.new(6) + assert_equal 6, game.players.length + 0.upto(5) do |i| + assert_equal $INITIAL_CARDS_PER_PLAYER, game.players_cards[i].length + end + assert_equal $SYMBOLS.length * $CARDS_PER_SYMBOL - 6 * $INITIAL_CARDS_PER_PLAYER, game.deck.length + end + + def test_game_setting + game = Game.new(5,6,6) + game.set_testing_game! + game.pieces.each do |player| + player.each do |piece| + assert_equal game.board.positions[0], piece.position + end + end + assert_equal [:cell, :gun, :keys, :dagger, :hat, :skull, :bottle, + :keys, :dagger, :skull, :hat, :gun, :bottle, + :dagger, :bottle, :keys, :gun, :hat, :skull, + :dagger, :skull, :bottle, :gun, :keys, :hat, + :hat, :dagger, :keys, :bottle, :gun, :skull, + :keys, :bottle, :skull, :dagger, :hat, :gun, :boat], + game.board.positions.collect {|p| p.symbol} + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} +# assert_equal [:bottle, :keys, :skull], game.players_cards[5].sort_by {|c| c.to_s} + assert_equal 5, game.players_cards.length + assert_equal 5, game.players.length + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle, :skull], + game.deck + assert_equal 0, game.moves_by_current_player + assert_equal 18, game.possible_moves.length + game.apply_move!(game.possible_moves[0]) + assert_equal [:hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.board.positions.index(game.pieces[0][0].position) + assert_equal 87, game.deck.length + + assert_equal 1, game.moves_by_current_player + assert_equal 12, game.possible_moves.length + game.apply_move!(game.possible_moves[3]) + assert_equal [:hat], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.board.positions.index(game.pieces[0][0].position) + assert_equal 5, game.board.positions.index(game.pieces[0][1].position) + assert_equal 87, game.deck.length + + assert_equal 2, game.moves_by_current_player + assert_equal 7, game.possible_moves.length + game.apply_move!(game.possible_moves[2]) + assert_equal [:hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.board.positions.index(game.pieces[0][0].position) + assert_equal 1, game.board.positions.index(game.pieces[0][1].position) + assert_equal 86, game.deck.length + + game.reset_current_player 0 # Needed to prevent errors below + + assert_equal 0, game.moves_by_current_player + assert_equal 12, game.possible_moves.length + game.apply_move!(game.possible_moves[-1]) + assert_equal [:hat], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.board.positions.index(game.pieces[0][0].position) + assert_equal 1, game.board.positions.index(game.pieces[0][1].position) + assert_equal 5, game.board.positions.index(game.pieces[0][5].position) + assert_equal 86, game.deck.length + + assert_equal 1, game.moves_by_current_player + assert_equal 7, game.possible_moves.length + game.apply_move!(game.possible_moves[-1]) + assert_equal [:bottle, :hat, :keys], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.board.positions.index(game.pieces[0][0].position) + assert_equal 1, game.board.positions.index(game.pieces[0][1].position) + assert_equal 1, game.board.positions.index(game.pieces[0][5].position) + assert_equal 84, game.deck.length + end + + def test_game_termination + game = Game.new(6) + game.set_testing_game! + assert_equal 0, game.moves_by_current_player + + game.apply_move!(Move.new(game.pieces[0][0], game.board.positions[0], game.board.positions[36]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[0].contains.include?(game.pieces[0][1]) + assert game.board.positions[0].contains.include?(game.pieces[0][2]) + assert game.board.positions[0].contains.include?(game.pieces[0][3]) + assert game.board.positions[0].contains.include?(game.pieces[0][4]) + assert game.board.positions[0].contains.include?(game.pieces[0][5]) + assert_equal 18, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][1], game.board.positions[0], game.board.positions[35]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[0].contains.include?(game.pieces[0][2]) + assert game.board.positions[0].contains.include?(game.pieces[0][3]) + assert game.board.positions[0].contains.include?(game.pieces[0][4]) + assert game.board.positions[0].contains.include?(game.pieces[0][5]) + assert_equal 19, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][2], game.board.positions[0], game.board.positions[34]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[34].contains.include?(game.pieces[0][2]) + assert game.board.positions[0].contains.include?(game.pieces[0][3]) + assert game.board.positions[0].contains.include?(game.pieces[0][4]) + assert game.board.positions[0].contains.include?(game.pieces[0][5]) + assert_equal 20, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][3], game.board.positions[0], game.board.positions[33]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[34].contains.include?(game.pieces[0][2]) + assert game.board.positions[33].contains.include?(game.pieces[0][3]) + assert game.board.positions[0].contains.include?(game.pieces[0][4]) + assert game.board.positions[0].contains.include?(game.pieces[0][5]) + assert_equal 21, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][4], game.board.positions[0], game.board.positions[32]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[34].contains.include?(game.pieces[0][2]) + assert game.board.positions[33].contains.include?(game.pieces[0][3]) + assert game.board.positions[32].contains.include?(game.pieces[0][4]) + assert game.board.positions[0].contains.include?(game.pieces[0][5]) + assert_equal 22, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][5], game.board.positions[0], game.board.positions[31]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[34].contains.include?(game.pieces[0][2]) + assert game.board.positions[33].contains.include?(game.pieces[0][3]) + assert game.board.positions[32].contains.include?(game.pieces[0][4]) + assert game.board.positions[31].contains.include?(game.pieces[0][5]) + assert_equal 23, game.possible_moves.length + + # Now start moving pices onto the boat + game.apply_move!(Move.new(game.pieces[0][5], game.board.positions[31], game.board.positions[37]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[34].contains.include?(game.pieces[0][2]) + assert game.board.positions[33].contains.include?(game.pieces[0][3]) + assert game.board.positions[32].contains.include?(game.pieces[0][4]) + assert game.board.positions[37].contains.include?(game.pieces[0][5]) + assert_equal 20, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][4], game.board.positions[32], game.board.positions[37]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[34].contains.include?(game.pieces[0][2]) + assert game.board.positions[33].contains.include?(game.pieces[0][3]) + assert game.board.positions[37].contains.include?(game.pieces[0][4]) + assert game.board.positions[37].contains.include?(game.pieces[0][5]) + assert_equal 17, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][3], game.board.positions[33], game.board.positions[37]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[34].contains.include?(game.pieces[0][2]) + assert game.board.positions[37].contains.include?(game.pieces[0][3]) + assert game.board.positions[37].contains.include?(game.pieces[0][4]) + assert game.board.positions[37].contains.include?(game.pieces[0][5]) + assert_equal 14, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][2], game.board.positions[34], game.board.positions[37]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[37].contains.include?(game.pieces[0][2]) + assert game.board.positions[37].contains.include?(game.pieces[0][3]) + assert game.board.positions[37].contains.include?(game.pieces[0][4]) + assert game.board.positions[37].contains.include?(game.pieces[0][5]) + assert_equal 11, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][1], game.board.positions[35], game.board.positions[37]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[37].contains.include?(game.pieces[0][1]) + assert game.board.positions[37].contains.include?(game.pieces[0][2]) + assert game.board.positions[37].contains.include?(game.pieces[0][3]) + assert game.board.positions[37].contains.include?(game.pieces[0][4]) + assert game.board.positions[37].contains.include?(game.pieces[0][5]) + assert_equal 8, game.possible_moves.length + + assert_raise(GameWonNotice) {game.apply_move!(Move.new(game.pieces[0][0], game.board.positions[36], game.board.positions[37]), 0, false)} + + end + + def test_retreats + game = Game.new(5,6,6) + game.set_testing_game! + + game.pieces.each do |player| + player.each do |piece| + assert_equal game.board.positions[0], piece.position + end + end + assert_equal [:cell, :gun, :keys, :dagger, :hat, :skull, :bottle, + :keys, :dagger, :skull, :hat, :gun, :bottle, + :dagger, :bottle, :keys, :gun, :hat, :skull, + :dagger, :skull, :bottle, :gun, :keys, :hat, + :hat, :dagger, :keys, :bottle, :gun, :skull, + :keys, :bottle, :skull, :dagger, :hat, :gun, :boat], + game.board.positions.collect {|p| p.symbol} + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} +# assert_equal [:bottle, :keys, :skull], game.players_cards[5].sort_by {|c| c.to_s} + assert_equal 5, game.players_cards.length + assert_equal 5, game.players.length + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle, :skull], + game.deck + assert_equal 0, game.moves_by_current_player + assert_equal 18, game.possible_moves.length + game.apply_move!(game.possible_moves[0]) + assert_equal [:hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.board.positions.index(game.pieces[0][0].position) + assert_equal 87, game.deck.length + + assert_equal 1, game.moves_by_current_player + assert_equal 12, game.possible_moves.length + game.apply_move!(game.possible_moves[3]) + assert_equal [:hat], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.board.positions.index(game.pieces[0][0].position) + assert_equal 5, game.board.positions.index(game.pieces[0][1].position) + assert_equal 87, game.deck.length + + # Test can't retreat into the cell + assert_equal 2, game.moves_by_current_player + assert_equal 7, game.possible_moves.length + assert_raise(InvalidMoveError) do + game.apply_move!(Move.new(game.pieces[0][1], game.board.positions[5], game.board.positions[0])) + end + end + + # Test invalid move error trapping + def test_invalid_move_error_trapping + game = Game.new(5,6,6) + game.set_testing_game! + + game.pieces.each do |player| + player.each do |piece| + assert_equal game.board.positions[0], piece.position + end + end + assert_equal [:cell, :gun, :keys, :dagger, :hat, :skull, :bottle, + :keys, :dagger, :skull, :hat, :gun, :bottle, + :dagger, :bottle, :keys, :gun, :hat, :skull, + :dagger, :skull, :bottle, :gun, :keys, :hat, + :hat, :dagger, :keys, :bottle, :gun, :skull, + :keys, :bottle, :skull, :dagger, :hat, :gun, :boat], + game.board.positions.collect {|p| p.symbol} + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal 5, game.players_cards.length + assert_equal 5, game.players.length + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle, :skull], + game.deck + assert_equal 0, game.moves_by_current_player + + # Test if can move to a space without the right card + assert_raise(InvalidMoveError) {game.apply_move!(Move.new(game.pieces[0][0], game.board.positions[2], game.board.positions[0]))} + + + # Apply a series of moves to get an interesting game state + game.apply_move!('0 0 5'.to_move(game)) + assert_equal [:gun, :hat], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 0 4'.to_move(game)) + assert_equal [:gun], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 5 4'.to_move(game)) + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 1, game.current_player + game.apply_move!('1 0 2'.to_move(game)) + assert_equal [:bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 0 7'.to_move(game)) + assert_equal [:bottle], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 7 4'.to_move(game)) + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 2, game.current_player + game.apply_move!('2 0 7'.to_move(game)) + assert_equal [:bottle, :hat], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 0 10'.to_move(game)) + assert_equal [:bottle], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 0 6'.to_move(game)) + assert_equal [], game.players_cards[2].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 3, game.current_player + game.apply_move!('3 0 12'.to_move(game)) + assert_equal [:skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 12 10'.to_move(game)) + assert_equal [:skull, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 0 5'.to_move(game)) + assert_equal [:skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 4, game.current_player + game.apply_move!('4 0 12'.to_move(game)) + assert_equal [:gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 12 10'.to_move(game)) + assert_equal [:dagger, :gun, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 0 3'.to_move(game)) + assert_equal [:gun, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:gun, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 0, game.current_player + + game.apply_move!('0 0 9'.to_move(game)) + assert_equal [:gun], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 0 1'.to_move(game)) + assert_equal [], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 9 7'.to_move(game)) + assert_equal [:keys], game.players_cards[0].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 1, game.current_player + game.apply_move!('1 0 12'.to_move(game)) + assert_equal [:bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 0 14'.to_move(game)) + assert_equal [:keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 12 7'.to_move(game)) + assert_equal [:hat, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 2, game.current_player + game.apply_move!('2 6 5'.to_move(game)) + assert_equal [:gun], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 0 11'.to_move(game)) + assert_equal [], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 11 5'.to_move(game)) + assert_equal [:bottle, :keys], game.players_cards[2].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 3, game.current_player + game.apply_move!('3 0 9'.to_move(game)) + assert_equal [:skull], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 0 18'.to_move(game)) + assert_equal [], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 18 14'.to_move(game)) + assert_equal [:bottle], game.players_cards[3].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 4, game.current_player + game.apply_move!('4 0 11'.to_move(game)) + assert_equal [:gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 0 16'.to_move(game)) + assert_equal [:gun], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 16 14'.to_move(game)) + assert_equal [:gun, :keys, :skull], game.players_cards[4].sort_by {|c| c.to_s} + + assert_equal [:keys], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:hat, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:gun, :keys, :skull], game.players_cards[4].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 0, game.current_player + + game.apply_move!('0 0 15'.to_move(game)) + assert_equal [], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 15 11'.to_move(game)) + assert_equal [:bottle], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 11 9'.to_move(game)) + assert_equal [:bottle, :skull], game.players_cards[0].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 1, game.current_player + game.apply_move!('1 0 15'.to_move(game)) + assert_equal [:hat, :keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 0 17'.to_move(game)) + assert_equal [:keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 17 15'.to_move(game)) + assert_equal [:keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 2, game.current_player + game.apply_move!('2 0 23'.to_move(game)) + assert_equal [:bottle], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 23 15'.to_move(game)) + assert_equal [:bottle, :dagger, :hat], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 0 17'.to_move(game)) + assert_equal [:bottle, :dagger], game.players_cards[2].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 3, game.current_player + game.apply_move!('3 10 9'.to_move(game)) + assert_equal [:bottle, :dagger, :hat], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 0 24'.to_move(game)) + assert_equal [:bottle, :dagger], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 0 8'.to_move(game)) + assert_equal [:bottle], game.players_cards[3].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 4, game.current_player + game.apply_move!('4 0 16'.to_move(game)) + assert_equal [:keys, :skull], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 16 11'.to_move(game)) + assert_equal [:keys, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 11 10'.to_move(game)) + assert_equal [:dagger, :dagger, :keys, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + + assert_equal 2, game.board.positions[ 0].contains.length + assert_equal 1, game.board.positions[ 1].contains.length + assert_equal 1, game.board.positions[ 2].contains.length + assert_equal 1, game.board.positions[ 3].contains.length + assert_equal 3, game.board.positions[ 4].contains.length + assert_equal 3, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 3, game.board.positions[ 7].contains.length + assert_equal 1, game.board.positions[ 8].contains.length + assert_equal 3, game.board.positions[ 9].contains.length + assert_equal 3, game.board.positions[10].contains.length + assert_equal 1, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 3, game.board.positions[14].contains.length + assert_equal 3, game.board.positions[15].contains.length + assert_equal 0, game.board.positions[16].contains.length + assert_equal 1, game.board.positions[17].contains.length + assert_equal 0, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 0, game.board.positions[20].contains.length + assert_equal 0, game.board.positions[21].contains.length + assert_equal 0, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 1, game.board.positions[24].contains.length + assert_equal 0, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 0, game.board.positions[27].contains.length + assert_equal 0, game.board.positions[28].contains.length + assert_equal 0, game.board.positions[29].contains.length + assert_equal 0, game.board.positions[30].contains.length + assert_equal 0, game.board.positions[31].contains.length + assert_equal 0, game.board.positions[32].contains.length + assert_equal 0, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 0, game.board.positions[35].contains.length + assert_equal 0, game.board.positions[36].contains.length + assert_equal 0, game.board.positions[37].contains.length + + assert_raise(InvalidMoveError) {game.apply_move!('0 7 6'.to_move(game))} # can't retreat into an empty space + assert_raise(InvalidMoveError) {game.apply_move!('0 7 5'.to_move(game))} # can't retreat into a space with three men + assert_raise(InvalidMoveError) {game.apply_move!('0 1 0'.to_move(game))} # can't retreat into the boat + assert_raise(InvalidMoveError) {game.apply_move!('0 1 12'.to_move(game))} # can't skip a vacant space of this type + assert_raise(InvalidMoveError) {game.apply_move!('0 1 5'.to_move(game))} # can't advance to a space with three pirates + assert_raise(InvalidMoveError) {game.apply_move!('0 2 6'.to_move(game))} # can't move another's piece + assert_raise(InvalidMoveError) {game.apply_move!('0 9 9'.to_move(game))} # can't advance the same square + assert_raise(InvalidMoveError) {game.apply_move!('0 9 20'.to_move(game))} # can't skip a vacant space of this type + + game.next_player! + assert_equal 0, game.current_player + + game.apply_move!('0 0 18'.to_move(game)) + assert_equal [:bottle], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 7 12'.to_move(game)) + assert_equal [], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 12 11'.to_move(game)) + assert_equal [:dagger], game.players_cards[0].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 1, game.current_player + game.apply_move!('1 14 11'.to_move(game)) + assert_equal [:dagger, :keys, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 2 23'.to_move(game)) + assert_equal [:dagger, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 15 14'.to_move(game)) + assert_equal [:bottle, :bottle, :dagger, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 2, game.current_player + game.apply_move!('2 17 15'.to_move(game)) + assert_equal [:bottle, :dagger, :dagger, :keys], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 5 27'.to_move(game)) + assert_equal [:bottle, :dagger, :dagger], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 5 13'.to_move(game)) + assert_equal [:bottle, :dagger], game.players_cards[2].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 3, game.current_player + game.apply_move!('3 8 7'.to_move(game)) + assert_equal [:bottle, :bottle, :hat], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 5 17'.to_move(game)) + assert_equal [:bottle, :bottle], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 7 12'.to_move(game)) + assert_equal [:bottle], game.players_cards[3].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 4, game.current_player + game.apply_move!('4 0 8'.to_move(game)) + assert_equal [:dagger, :keys, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 8 7'.to_move(game)) + assert_equal [:bottle, :dagger, :keys, :keys, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 3 31'.to_move(game)) + assert_equal [:bottle, :dagger, :keys, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + + + game.next_player! + assert_equal 0, game.current_player + + game.apply_move!('0 18 17'.to_move(game)) + assert_equal [:dagger, :gun], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 1 16'.to_move(game)) + assert_equal [:dagger], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 17 16'.to_move(game)) + assert_equal [:dagger, :skull], game.players_cards[0].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 1, game.current_player + game.apply_move!('1 4 37 keys'.to_move(game)) + assert_equal [:bottle, :bottle, :dagger, :keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 7 4'.to_move(game)) + assert_equal [:bottle, :bottle, :bottle, :dagger, :hat, :keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 4 37 keys'.to_move(game)) + assert_equal [:bottle, :bottle, :bottle, :dagger, :hat], game.players_cards[1].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 2, game.current_player + game.apply_move!('2 7 21'.to_move(game)) + assert_equal [:dagger], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 13 12'.to_move(game)) + assert_equal [:dagger, :dagger], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 10 13'.to_move(game)) + assert_equal [:dagger], game.players_cards[2].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 3, game.current_player + game.apply_move!('3 17 16'.to_move(game)) + assert_equal [:bottle, :hat, :skull], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 9 28'.to_move(game)) + assert_equal [:hat, :skull], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 9 18'.to_move(game)) + assert_equal [:hat], game.players_cards[3].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 4, game.current_player + game.apply_move!('4 7 37 keys'.to_move(game)) + assert_equal [:bottle, :dagger, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 11 10'.to_move(game)) + assert_equal [:bottle, :dagger, :dagger, :gun, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 10 32'.to_move(game)) + assert_equal [:dagger, :dagger, :gun, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + + + game.next_player! + assert_equal 0, game.current_player + + game.apply_move!('0 11 10'.to_move(game)) + assert_equal [:bottle, :dagger, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 4 8'.to_move(game)) + assert_equal [:bottle, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 4 17'.to_move(game)) + assert_equal [:bottle, :skull], game.players_cards[0].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 1, game.current_player + game.apply_move!('1 11 37 bottle'.to_move(game)) + assert_equal [:bottle, :bottle, :dagger, :hat], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 14 37 bottle'.to_move(game)) + assert_equal [:bottle, :dagger, :hat], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 15 37 bottle'.to_move(game)) + assert_equal [:dagger, :hat], game.players_cards[1].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 2, game.current_player + game.apply_move!('2 13 12'.to_move(game)) + assert_equal [:dagger, :gun, :hat], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 12 22 gun'.to_move(game)) + assert_equal [:dagger, :hat], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 12 25 hat'.to_move(game)) + assert_equal [:dagger], game.players_cards[2].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 3, game.current_player + game.apply_move!('3 16 15'.to_move(game)) + assert_equal [:dagger, :gun, :hat], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 12 35 hat'.to_move(game)) + assert_equal [:dagger, :gun], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 14 29 gun'.to_move(game)) + assert_equal [:dagger], game.players_cards[3].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 4, game.current_player + game.apply_move!('4 10 20 skull'.to_move(game)) + assert_equal [:dagger, :dagger, :gun, :skull], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 10 30 skull'.to_move(game)) + assert_equal [:dagger, :dagger, :gun], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 14 36 gun'.to_move(game)) + assert_equal [:dagger, :dagger], game.players_cards[4].sort_by {|c| c.to_s} + + + game.next_player! + assert_equal 0, game.current_player + + game.apply_move!('0 17 16'.to_move(game)) + assert_equal [:bottle, :gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 8 33 skull'.to_move(game)) + assert_equal [:bottle, :gun, :hat], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 9 17 hat'.to_move(game)) + assert_equal [:bottle, :gun], game.players_cards[0].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 1, game.current_player + assert_raise(GameWonNotice) {game.apply_move!('1 23 37 hat'.to_move(game))} + assert_equal [:dagger], game.players_cards[1].sort_by {|c| c.to_s} + + assert_equal 0, game.board.positions[ 0].contains.length + assert_equal 0, game.board.positions[ 1].contains.length + assert_equal 0, game.board.positions[ 2].contains.length + assert_equal 0, game.board.positions[ 3].contains.length + assert_equal 0, game.board.positions[ 4].contains.length + assert_equal 0, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 0, game.board.positions[ 7].contains.length + assert_equal 0, game.board.positions[ 8].contains.length + assert_equal 0, game.board.positions[ 9].contains.length + assert_equal 1, game.board.positions[10].contains.length + assert_equal 0, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 0, game.board.positions[14].contains.length + assert_equal 3, game.board.positions[15].contains.length + assert_equal 3, game.board.positions[16].contains.length + assert_equal 1, game.board.positions[17].contains.length + assert_equal 1, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 1, game.board.positions[20].contains.length + assert_equal 1, game.board.positions[21].contains.length + assert_equal 1, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 1, game.board.positions[24].contains.length + assert_equal 1, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 1, game.board.positions[27].contains.length + assert_equal 1, game.board.positions[28].contains.length + assert_equal 1, game.board.positions[29].contains.length + assert_equal 1, game.board.positions[30].contains.length + assert_equal 1, game.board.positions[31].contains.length + assert_equal 1, game.board.positions[32].contains.length + assert_equal 1, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 1, game.board.positions[35].contains.length + assert_equal 1, game.board.positions[36].contains.length + assert_equal 7, game.board.positions[37].contains.length + + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun], + game.deck + + assert_equal [:bottle, :gun], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:dagger], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:dagger], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:dagger], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:dagger, :dagger], game.players_cards[4].sort_by {|c| c.to_s} + + end + # + # : move without a card + # move to an occupied space + # retreat to an emtpy space + # retreat to an overfull space + # retreat to the cell with one pirate already in it + + + # Test current player switching + + # Test undo moves + + # Test file reading + # (need a full game file to read) + + def test_undo + game = Game.new(5,6,6) + game.set_testing_game! + game.pieces.each do |player| + player.each do |piece| + assert_equal game.board.positions[0], piece.position + end + end + assert_equal [:cell, :gun, :keys, :dagger, :hat, :skull, :bottle, + :keys, :dagger, :skull, :hat, :gun, :bottle, + :dagger, :bottle, :keys, :gun, :hat, :skull, + :dagger, :skull, :bottle, :gun, :keys, :hat, + :hat, :dagger, :keys, :bottle, :gun, :skull, + :keys, :bottle, :skull, :dagger, :hat, :gun, :boat], + game.board.positions.collect {|p| p.symbol} + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal 5, game.players_cards.length + assert_equal 5, game.players.length + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle, :skull], + game.deck + + assert_equal 0, game.history.length + + game.apply_move!('0 0 5'.to_move(game)) + assert_equal [:gun, :hat], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.history.length + game.apply_move!('0 0 4'.to_move(game)) + assert_equal [:gun], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 2, game.history.length + game.apply_move!('0 5 4'.to_move(game)) + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 3, game.history.length + + game.next_player! + assert_equal 1, game.current_player + assert_equal 3, game.history.length + game.apply_move!('1 0 2'.to_move(game)) + assert_equal [:bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal 4, game.history.length + game.apply_move!('1 0 7'.to_move(game)) + assert_equal [:bottle], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal 5, game.history.length + game.apply_move!('1 7 4'.to_move(game)) + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal 6, game.history.length + assert_equal 1, game.current_player + assert_equal 3, game.moves_by_current_player + + assert_equal 26, game.board.positions[ 0].contains.length + assert_equal 0, game.board.positions[ 1].contains.length + assert_equal 1, game.board.positions[ 2].contains.length + assert_equal 0, game.board.positions[ 3].contains.length + assert_equal 3, game.board.positions[ 4].contains.length + assert_equal 0, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 0, game.board.positions[ 7].contains.length + assert_equal 0, game.board.positions[ 8].contains.length + assert_equal 0, game.board.positions[ 9].contains.length + assert_equal 0, game.board.positions[10].contains.length + assert_equal 0, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 0, game.board.positions[14].contains.length + assert_equal 0, game.board.positions[15].contains.length + assert_equal 0, game.board.positions[16].contains.length + assert_equal 0, game.board.positions[17].contains.length + assert_equal 0, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 0, game.board.positions[20].contains.length + assert_equal 0, game.board.positions[21].contains.length + assert_equal 0, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 0, game.board.positions[24].contains.length + assert_equal 0, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 0, game.board.positions[27].contains.length + assert_equal 0, game.board.positions[28].contains.length + assert_equal 0, game.board.positions[29].contains.length + assert_equal 0, game.board.positions[30].contains.length + assert_equal 0, game.board.positions[31].contains.length + assert_equal 0, game.board.positions[32].contains.length + assert_equal 0, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 0, game.board.positions[35].contains.length + assert_equal 0, game.board.positions[36].contains.length + assert_equal 0, game.board.positions[37].contains.length + + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull], + game.deck + + game.undo_move! # player 1's third move + + assert_equal 5, game.history.length + + assert_equal 26, game.board.positions[ 0].contains.length + assert_equal 0, game.board.positions[ 1].contains.length + assert_equal 1, game.board.positions[ 2].contains.length + assert_equal 0, game.board.positions[ 3].contains.length + assert_equal 2, game.board.positions[ 4].contains.length + assert_equal 0, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 1, game.board.positions[ 7].contains.length + assert_equal 0, game.board.positions[ 8].contains.length + assert_equal 0, game.board.positions[ 9].contains.length + assert_equal 0, game.board.positions[10].contains.length + assert_equal 0, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 0, game.board.positions[14].contains.length + assert_equal 0, game.board.positions[15].contains.length + assert_equal 0, game.board.positions[16].contains.length + assert_equal 0, game.board.positions[17].contains.length + assert_equal 0, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 0, game.board.positions[20].contains.length + assert_equal 0, game.board.positions[21].contains.length + assert_equal 0, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 0, game.board.positions[24].contains.length + assert_equal 0, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 0, game.board.positions[27].contains.length + assert_equal 0, game.board.positions[28].contains.length + assert_equal 0, game.board.positions[29].contains.length + assert_equal 0, game.board.positions[30].contains.length + assert_equal 0, game.board.positions[31].contains.length + assert_equal 0, game.board.positions[32].contains.length + assert_equal 0, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 0, game.board.positions[35].contains.length + assert_equal 0, game.board.positions[36].contains.length + assert_equal 0, game.board.positions[37].contains.length + + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle], + game.deck + assert_equal 1, game.current_player + assert_equal 2, game.moves_by_current_player + + # Re-apply player 1's third move + game.apply_move!('1 7 4'.to_move(game)) + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 2, game.current_player + game.apply_move!('2 0 7'.to_move(game)) + assert_equal [:bottle, :hat], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 0 10'.to_move(game)) + assert_equal [:bottle], game.players_cards[2].sort_by {|c| c.to_s} + + assert_equal 8, game.history.length + + assert_equal 24, game.board.positions[ 0].contains.length + assert_equal 0, game.board.positions[ 1].contains.length + assert_equal 1, game.board.positions[ 2].contains.length + assert_equal 0, game.board.positions[ 3].contains.length + assert_equal 3, game.board.positions[ 4].contains.length + assert_equal 0, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 1, game.board.positions[ 7].contains.length + assert_equal 0, game.board.positions[ 8].contains.length + assert_equal 0, game.board.positions[ 9].contains.length + assert_equal 1, game.board.positions[10].contains.length + assert_equal 0, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 0, game.board.positions[14].contains.length + assert_equal 0, game.board.positions[15].contains.length + assert_equal 0, game.board.positions[16].contains.length + assert_equal 0, game.board.positions[17].contains.length + assert_equal 0, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 0, game.board.positions[20].contains.length + assert_equal 0, game.board.positions[21].contains.length + assert_equal 0, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 0, game.board.positions[24].contains.length + assert_equal 0, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 0, game.board.positions[27].contains.length + assert_equal 0, game.board.positions[28].contains.length + assert_equal 0, game.board.positions[29].contains.length + assert_equal 0, game.board.positions[30].contains.length + assert_equal 0, game.board.positions[31].contains.length + assert_equal 0, game.board.positions[32].contains.length + assert_equal 0, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 0, game.board.positions[35].contains.length + assert_equal 0, game.board.positions[36].contains.length + assert_equal 0, game.board.positions[37].contains.length + + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull], + game.deck + assert_equal 2, game.current_player + assert_equal 2, game.moves_by_current_player + + game.undo_move! # player 2's second move + + assert_equal 7, game.history.length + + assert_equal 25, game.board.positions[ 0].contains.length + assert_equal 0, game.board.positions[ 1].contains.length + assert_equal 1, game.board.positions[ 2].contains.length + assert_equal 0, game.board.positions[ 3].contains.length + assert_equal 3, game.board.positions[ 4].contains.length + assert_equal 0, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 1, game.board.positions[ 7].contains.length + assert_equal 0, game.board.positions[ 8].contains.length + assert_equal 0, game.board.positions[ 9].contains.length + assert_equal 0, game.board.positions[10].contains.length + assert_equal 0, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 0, game.board.positions[14].contains.length + assert_equal 0, game.board.positions[15].contains.length + assert_equal 0, game.board.positions[16].contains.length + assert_equal 0, game.board.positions[17].contains.length + assert_equal 0, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 0, game.board.positions[20].contains.length + assert_equal 0, game.board.positions[21].contains.length + assert_equal 0, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 0, game.board.positions[24].contains.length + assert_equal 0, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 0, game.board.positions[27].contains.length + assert_equal 0, game.board.positions[28].contains.length + assert_equal 0, game.board.positions[29].contains.length + assert_equal 0, game.board.positions[30].contains.length + assert_equal 0, game.board.positions[31].contains.length + assert_equal 0, game.board.positions[32].contains.length + assert_equal 0, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 0, game.board.positions[35].contains.length + assert_equal 0, game.board.positions[36].contains.length + assert_equal 0, game.board.positions[37].contains.length + + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull], + game.deck + assert_equal 2, game.current_player + assert_equal 1, game.moves_by_current_player + + + game.undo_move! # Player 2's first move + + assert_equal 6, game.history.length + + assert_equal 26, game.board.positions[ 0].contains.length + assert_equal 0, game.board.positions[ 1].contains.length + assert_equal 1, game.board.positions[ 2].contains.length + assert_equal 0, game.board.positions[ 3].contains.length + assert_equal 3, game.board.positions[ 4].contains.length + assert_equal 0, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 0, game.board.positions[ 7].contains.length + assert_equal 0, game.board.positions[ 8].contains.length + assert_equal 0, game.board.positions[ 9].contains.length + assert_equal 0, game.board.positions[10].contains.length + assert_equal 0, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 0, game.board.positions[14].contains.length + assert_equal 0, game.board.positions[15].contains.length + assert_equal 0, game.board.positions[16].contains.length + assert_equal 0, game.board.positions[17].contains.length + assert_equal 0, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 0, game.board.positions[20].contains.length + assert_equal 0, game.board.positions[21].contains.length + assert_equal 0, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 0, game.board.positions[24].contains.length + assert_equal 0, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 0, game.board.positions[27].contains.length + assert_equal 0, game.board.positions[28].contains.length + assert_equal 0, game.board.positions[29].contains.length + assert_equal 0, game.board.positions[30].contains.length + assert_equal 0, game.board.positions[31].contains.length + assert_equal 0, game.board.positions[32].contains.length + assert_equal 0, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 0, game.board.positions[35].contains.length + assert_equal 0, game.board.positions[36].contains.length + assert_equal 0, game.board.positions[37].contains.length + + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull], + game.deck + assert_equal 2, game.current_player + assert_equal 0, game.moves_by_current_player + + + game.undo_move! # Player 1's third move + + assert_equal 5, game.history.length + + assert_equal 26, game.board.positions[ 0].contains.length + assert_equal 0, game.board.positions[ 1].contains.length + assert_equal 1, game.board.positions[ 2].contains.length + assert_equal 0, game.board.positions[ 3].contains.length + assert_equal 2, game.board.positions[ 4].contains.length + assert_equal 0, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 1, game.board.positions[ 7].contains.length + assert_equal 0, game.board.positions[ 8].contains.length + assert_equal 0, game.board.positions[ 9].contains.length + assert_equal 0, game.board.positions[10].contains.length + assert_equal 0, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 0, game.board.positions[14].contains.length + assert_equal 0, game.board.positions[15].contains.length + assert_equal 0, game.board.positions[16].contains.length + assert_equal 0, game.board.positions[17].contains.length + assert_equal 0, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 0, game.board.positions[20].contains.length + assert_equal 0, game.board.positions[21].contains.length + assert_equal 0, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 0, game.board.positions[24].contains.length + assert_equal 0, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 0, game.board.positions[27].contains.length + assert_equal 0, game.board.positions[28].contains.length + assert_equal 0, game.board.positions[29].contains.length + assert_equal 0, game.board.positions[30].contains.length + assert_equal 0, game.board.positions[31].contains.length + assert_equal 0, game.board.positions[32].contains.length + assert_equal 0, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 0, game.board.positions[35].contains.length + assert_equal 0, game.board.positions[36].contains.length + assert_equal 0, game.board.positions[37].contains.length + + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle], + game.deck + assert_equal 1, game.current_player + assert_equal 2, game.moves_by_current_player + + end + + def test_apply_moves + game = Game.new(5,6,6) + game.set_testing_game! + game.pieces.each do |player| + player.each do |piece| + assert_equal game.board.positions[0], piece.position + end + end + assert_equal [:cell, :gun, :keys, :dagger, :hat, :skull, :bottle, + :keys, :dagger, :skull, :hat, :gun, :bottle, + :dagger, :bottle, :keys, :gun, :hat, :skull, + :dagger, :skull, :bottle, :gun, :keys, :hat, + :hat, :dagger, :keys, :bottle, :gun, :skull, + :keys, :bottle, :skull, :dagger, :hat, :gun, :boat], + game.board.positions.collect {|p| p.symbol} + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal 5, game.players_cards.length + assert_equal 5, game.players.length + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle, :skull], + game.deck + + assert_equal 0, game.history.length + + game.apply_move!('0 0 5'.to_move(game)) + assert_equal [:gun, :hat], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.history.length + game.apply_move!('0 0 4'.to_move(game)) + assert_equal [:gun], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 2, game.history.length + game.apply_move!('0 5 4'.to_move(game)) + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 3, game.history.length + + game.next_player! + assert_equal 1, game.current_player + assert_equal 3, game.history.length + game.apply_move!('1 0 2'.to_move(game)) + end + + def test_read_game_lines + gamelines1 = ["5", "cell", "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 5 skull", "1 0 4 hat", + "1", + "dagger"] + assert_nothing_raised {Game.read_game(gamelines1)} + game, moves = Game.read_game(gamelines1) + assert_equal 2, game.history.length + assert_equal 0, game.current_player + assert_equal 2, game.moves_by_current_player + assert_equal 1, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 15, game.deck.length + + game.apply_move!('0 5 4'.to_move(game)) + assert_equal 3, game.history.length + assert_equal 0, game.current_player + assert_equal 3, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 16, game.deck.length + + gamelines2 = ["5", "cell", "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 5", "1 0 4", "1 5 4", + "2", + "dagger", "hat", "keys"] + assert_nothing_raised {Game.read_game(gamelines2)} + game, moves = Game.read_game(gamelines2) + assert_equal 3, game.history.length + assert_equal 1, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 16, game.deck.length + + game.apply_move!('1 0 2'.to_move(game)) + assert_equal 4, game.history.length + assert_equal 1, game.current_player + assert_equal 1, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 2, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 16, game.deck.length + + gamelines3 = ["5", "cell", "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 5", "1 0 4", "1 5 4", "2 0 2", + "2", + "dagger", "keys"] + assert_nothing_raised {Game.read_game(gamelines3)} + game, moves = Game.read_game(gamelines3) + assert_equal 4, game.history.length + assert_equal 1, game.current_player + assert_equal 1, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 2, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 16, game.deck.length + + game.apply_move!('1 0 7'.to_move(game)) + assert_equal 5, game.history.length + assert_equal 1, game.current_player + assert_equal 2, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 1, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 16, game.deck.length + + gamelines4 = ["5", "cell", "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 5", "1 0 4", "1 5 4", "2 0 2", "2 0 7", + "2", + "dagger"] + assert_nothing_raised {Game.read_game(gamelines4)} + game, moves = Game.read_game(gamelines4) + assert_equal 5, game.history.length + assert_equal 1, game.current_player + assert_equal 2, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 1, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 16, game.deck.length + + game.apply_move!('1 7 4'.to_move(game)) + assert_equal 6, game.history.length + assert_equal 1, game.current_player + assert_equal 3, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 18, game.deck.length + + gamelines5 = ["5", "cell", "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 5", "1 0 4", "1 5 4", "2 0 2", "2 0 7", "2 7 4", + "2", + "bottle", "hat", "keys"] + assert_nothing_raised {Game.read_game(gamelines5)} + game, moves = Game.read_game(gamelines5) + assert_equal 6, game.history.length + assert_equal 1, game.current_player + assert_equal 3, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 18, game.deck.length + + game.players_cards[2] << :keys + game.players_cards[2] << :hat + game.apply_move!('3 0 7'.to_move(game, true), 2) + assert_equal 7, game.history.length + assert_equal 2, game.current_player + assert_equal 1, game.moves_by_current_player + game.apply_move!('3 0 10'.to_move(game, true)) + assert_equal 8, game.history.length + assert_equal 2, game.current_player + assert_equal 2, game.moves_by_current_player + + gamelines6 = ["5", "cell", "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 5", "1 0 4", "1 5 4", "2 0 2", "2 0 7", "2 7 4", + "3", + "bottle", "hat", "keys"] + assert_nothing_raised {Game.read_game(gamelines6)} + game, moves = Game.read_game(gamelines6) + assert_equal 6, game.history.length + assert_equal 2, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 18, game.deck.length + + game.apply_move!('2 0 7'.to_move(game)) + assert_equal 7, game.history.length + assert_equal 2, game.current_player + assert_equal 1, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 2, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 18, game.deck.length + + game.apply_move!('2 0 10'.to_move(game)) + assert_equal 8, game.history.length + assert_equal 2, game.current_player + assert_equal 2, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 1, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 18, game.deck.length + + # Need to do a test with a very long sequence of moves, + # to test that the card dealing works properly when the deck is exhausted + + end + + def test_dealing + game = Game.new(5,6,6) + game.set_testing_game! + 0.upto(4) do |i| + assert_equal $INITIAL_CARDS_PER_PLAYER, game.players_cards[i].length + end + assert_equal $SYMBOLS.length * $CARDS_PER_SYMBOL - 5 * $INITIAL_CARDS_PER_PLAYER, game.deck.length + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal 5, game.players_cards.length + assert_equal 5, game.players.length + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle, :skull], + game.deck + game.deal_cards!(86, 0) + assert_equal 89, game.players_cards[0].length + assert_equal 1, game.deck.length + assert_equal [:hat], game.deck + + game.players_cards[0] = [:hat, :hat, :hat, :hat, :hat] + game.players_cards[1] = [:keys, :keys, :keys] + game.players_cards[2] = [:bottle, :dagger, :skull] + game.players_cards[3] = [] + game.players_cards[4] = [] + game.deal_cards!(1, 3) + + assert_equal [:hat, :hat, :hat, :hat, :hat], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:keys, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :dagger, :skull], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:hat], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal [], game.deck + + game.deal_cards!(1, 4) + assert_equal [:keys], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal $CARDS_PER_SYMBOL - 6, game.deck.select {|c| c == :hat}.length + assert_equal $CARDS_PER_SYMBOL - 4, game.deck.select {|c| c == :keys}.length + assert_equal $CARDS_PER_SYMBOL - 1, game.deck.select {|c| c == :bottle}.length + assert_equal $CARDS_PER_SYMBOL - 1, game.deck.select {|c| c == :dagger}.length + assert_equal $CARDS_PER_SYMBOL - 1, game.deck.select {|c| c == :skull}.length + assert_equal $CARDS_PER_SYMBOL, game.deck.select {|c| c == :gun}.length + assert_equal $CARDS_PER_SYMBOL * 6 - 13, game.deck.length + + end + + def test_long_deal + gamelines0 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1", + "hat", "skull", "bottle"] + assert_nothing_raised {Game.read_game(gamelines0)} + game, moves = Game.read_game(gamelines0) + assert_equal 0, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 3, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 9, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 9, game.deck.length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :skull}).length + + gamelines1 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1 gun", "1 0 2 keys", "1 2 1", + "2", + "hat", "skull", "bottle"] + assert_nothing_raised {Game.read_game(gamelines1)} + game, moves = Game.read_game(gamelines1) + assert_equal 3, game.history.length + assert_equal 1, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 10, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 2, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 10, game.deck.length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 1, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 1, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :skull}).length + + gamelines2 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1", + "hat"] + assert_nothing_raised {Game.read_game(gamelines2)} + game, moves = Game.read_game(gamelines2) + assert_equal 18, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 1, game.players_cards[0].length + assert_equal 1, game.players_cards[1].length + assert_equal 1, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 6), game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 12, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 6), game.deck.length + assert_equal $CARDS_PER_SYMBOL - 2, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 2, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 2, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 2, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 2, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 2, (unplayed_cards.select {|c| c == :skull}).length + + gamelines2 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1", + "hat", "skull", "bottle"] + assert_nothing_raised {Game.read_game(gamelines2)} + game, moves = Game.read_game(gamelines2) + assert_equal 30, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 3, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 18), game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 18, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 18), game.deck.length + assert_equal $CARDS_PER_SYMBOL - 3, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 3, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 3, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 3, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 3, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 3, (unplayed_cards.select {|c| c == :skull}).length + + gamelines3 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1", + "hat", "skull", "bottle", "gun", "dagger"] + assert_nothing_raised {Game.read_game(gamelines3)} + game, moves = Game.read_game(gamelines3) + assert_equal 42, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 5, game.players_cards[0].length + assert_equal 5, game.players_cards[1].length + assert_equal 5, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 30), game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 24, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 30), game.deck.length + assert_equal $CARDS_PER_SYMBOL - 4, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 4, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 4, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 4, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 4, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 4, (unplayed_cards.select {|c| c == :skull}).length + + gamelines4 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1", + "hat", "skull", "bottle", "gun", "dagger", "keys", "hat"] + assert_nothing_raised {Game.read_game(gamelines4)} + game, moves = Game.read_game(gamelines4) + assert_equal 54, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 7, game.players_cards[0].length + assert_equal 7, game.players_cards[1].length + assert_equal 7, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 42), game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 30, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 42), game.deck.length + assert_equal $CARDS_PER_SYMBOL - 5, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 5, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 5, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 5, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 5, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 5, (unplayed_cards.select {|c| c == :skull}).length + + + gamelines5 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1", + "hat", "skull", "bottle", "gun", "dagger", "keys", "hat", "skull", "bottle", "gun", "dagger"] + assert_nothing_raised {Game.read_game(gamelines5)} + game, moves = Game.read_game(gamelines5) + assert_equal 78, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 11, game.players_cards[0].length + assert_equal 11, game.players_cards[1].length + assert_equal 11, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 66), game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 42, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 66), game.deck.length + assert_equal $CARDS_PER_SYMBOL - 7, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 7, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 7, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 7, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 7, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 7, (unplayed_cards.select {|c| c == :skull}).length + + gamelines6 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle"] + assert_nothing_raised {Game.read_game(gamelines6)} + game, moves = Game.read_game(gamelines6) + assert_equal 102, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 15, game.players_cards[0].length + assert_equal 15, game.players_cards[1].length + assert_equal 15, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 90), game.deck.length + assert_equal 3, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 54, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 90), game.deck.length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :skull}).length + + gamelines7 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", + "1", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle", "gun"] + assert_nothing_raised {Game.read_game(gamelines7)} + game, moves = Game.read_game(gamelines7) + assert_equal 104, game.history.length + assert_equal 0, game.current_player + assert_equal 2, game.moves_by_current_player + assert_equal 16, game.players_cards[0].length + assert_equal 15, game.players_cards[1].length + assert_equal 15, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 55, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :skull}).length + + gamelines8 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "1", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "skull", "bottle", "dagger"] + assert_nothing_raised {Game.read_game(gamelines8)} + game, moves = Game.read_game(gamelines8) + assert_equal 105, game.history.length + assert_equal 0, game.current_player + assert_equal 3, game.moves_by_current_player + assert_equal 15, game.players_cards[0].length + assert_equal 15, game.players_cards[1].length + assert_equal 15, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 56, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :skull}).length + + + gamelines9 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 12", "2 12 10", + "2", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle"] + assert_nothing_raised {Game.read_game(gamelines9)} + game, moves = Game.read_game(gamelines9) + assert_equal 107, game.history.length + assert_equal 1, game.current_player + assert_equal 2, game.moves_by_current_player + assert_equal 15, game.players_cards[0].length + assert_equal 15, game.players_cards[1].length + assert_equal 15, game.players_cards[2].length + assert_equal 0, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 56, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :skull}).length + + + end + +end \ No newline at end of file diff --git a/test/.svn/text-base/libcartagena_test.rb.svn-base b/test/.svn/text-base/libcartagena_test.rb.svn-base new file mode 100644 index 0000000..7825154 --- /dev/null +++ b/test/.svn/text-base/libcartagena_test.rb.svn-base @@ -0,0 +1,2920 @@ +# == Synopsis +# +# Unit tests for the libcartagena library +# +# == Author +# Neil Smith +# +# 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 . +# +# == Change history +# Version 1.0:: 5 Dec 2008 +# => * Initial build + + +$:.unshift File.join(File.dirname(__FILE__),'..','lib', 'libcartagena') + +require 'test/unit' +require File.join(File.dirname(__FILE__), '..', 'lib', 'libcartagena') + +class TileTest < Test::Unit::TestCase + def test_tile_init + front = [:hat, :keys, :gun, :bottle, :skull, :dagger] + back = [:gun, :hat, :dagger, :skull, :bottle, :keys] + assert_nothing_raised { Tile.new(front, back) } + tile = Tile.new(front, back) + + assert_equal 6, tile.front.length + assert_equal 6, tile.back.length + assert_equal 6, tile.exposed.length + assert_equal front, tile.front.map {|p| p.symbol} + assert_equal back, tile.back.map {|p| p.symbol} + assert_equal front, tile.exposed.map {|p| p.symbol} + + tile.flip! + assert_equal 6, tile.front.length + assert_equal 6, tile.back.length + assert_equal 6, tile.exposed.length + assert_equal front, tile.front.map {|p| p.symbol} + assert_equal back, tile.back.map {|p| p.symbol} + assert_equal back, tile.exposed.map {|p| p.symbol} + + tile.flip! + assert_equal 6, tile.front.length + assert_equal 6, tile.back.length + assert_equal 6, tile.exposed.length + assert_equal front, tile.front.map {|p| p.symbol} + assert_equal back, tile.back.map {|p| p.symbol} + assert_equal front, tile.exposed.map {|p| p.symbol} + + tile.flip! + tile.reverse! + assert_equal 6, tile.front.length + assert_equal 6, tile.back.length + assert_equal 6, tile.exposed.length + assert_equal front, tile.front.map {|p| p.symbol} + assert_equal back, tile.back.map {|p| p.symbol} + assert_equal back.reverse, tile.exposed.map {|p| p.symbol} + + tile.flip! + assert_equal 6, tile.front.length + assert_equal 6, tile.back.length + assert_equal 6, tile.exposed.length + assert_equal front, tile.front.map {|p| p.symbol} + assert_equal back, tile.back.map {|p| p.symbol} + assert_equal front, tile.exposed.map {|p| p.symbol} + + tile.reverse! + assert_equal 6, tile.front.length + assert_equal 6, tile.back.length + assert_equal 6, tile.exposed.length + assert_equal front, tile.front.map {|p| p.symbol} + assert_equal back, tile.back.map {|p| p.symbol} + assert_equal front.reverse, tile.exposed.map {|p| p.symbol} + + end +end + +class BoardTest < Test::Unit::TestCase + def test_board_init + assert_nothing_raised { Board.new(6) } + board = Board.new(1) + assert_equal 1, board.tiles.length + assert_equal 8, board.positions.length + + board = Board.new(6) + assert_equal 6, board.tiles.length + assert_equal 38, board.positions.length + + 0.upto(5) do |i| + start = i * 6 + 1 + slice = board.positions[start..(start+5)] + $SYMBOLS.each do |tile| + assert_equal 1, (slice.find_all {|p| p.symbol == tile}).length + end + end + end +end + + +class GameTest < Test::Unit::TestCase + def test_game_init + assert_nothing_raised { Game.new } + + game = Game.new(6) + assert_equal 6, game.players.length + 0.upto(5) do |i| + assert_equal $INITIAL_CARDS_PER_PLAYER, game.players_cards[i].length + end + assert_equal $SYMBOLS.length * $CARDS_PER_SYMBOL - 6 * $INITIAL_CARDS_PER_PLAYER, game.deck.length + end + + def test_game_setting + game = Game.new(5,6,6) + game.set_testing_game! + game.pieces.each do |player| + player.each do |piece| + assert_equal game.board.positions[0], piece.position + end + end + assert_equal [:cell, :gun, :keys, :dagger, :hat, :skull, :bottle, + :keys, :dagger, :skull, :hat, :gun, :bottle, + :dagger, :bottle, :keys, :gun, :hat, :skull, + :dagger, :skull, :bottle, :gun, :keys, :hat, + :hat, :dagger, :keys, :bottle, :gun, :skull, + :keys, :bottle, :skull, :dagger, :hat, :gun, :boat], + game.board.positions.collect {|p| p.symbol} + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} +# assert_equal [:bottle, :keys, :skull], game.players_cards[5].sort_by {|c| c.to_s} + assert_equal 5, game.players_cards.length + assert_equal 5, game.players.length + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle, :skull], + game.deck + assert_equal 0, game.moves_by_current_player + assert_equal 18, game.possible_moves.length + game.apply_move!(game.possible_moves[0]) + assert_equal [:hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.board.positions.index(game.pieces[0][0].position) + assert_equal 87, game.deck.length + + assert_equal 1, game.moves_by_current_player + assert_equal 12, game.possible_moves.length + game.apply_move!(game.possible_moves[3]) + assert_equal [:hat], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.board.positions.index(game.pieces[0][0].position) + assert_equal 5, game.board.positions.index(game.pieces[0][1].position) + assert_equal 87, game.deck.length + + assert_equal 2, game.moves_by_current_player + assert_equal 7, game.possible_moves.length + game.apply_move!(game.possible_moves[2]) + assert_equal [:hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.board.positions.index(game.pieces[0][0].position) + assert_equal 1, game.board.positions.index(game.pieces[0][1].position) + assert_equal 86, game.deck.length + + game.reset_current_player 0 # Needed to prevent errors below + + assert_equal 0, game.moves_by_current_player + assert_equal 12, game.possible_moves.length + game.apply_move!(game.possible_moves[-1]) + assert_equal [:hat], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.board.positions.index(game.pieces[0][0].position) + assert_equal 1, game.board.positions.index(game.pieces[0][1].position) + assert_equal 5, game.board.positions.index(game.pieces[0][5].position) + assert_equal 86, game.deck.length + + assert_equal 1, game.moves_by_current_player + assert_equal 7, game.possible_moves.length + game.apply_move!(game.possible_moves[-1]) + assert_equal [:bottle, :hat, :keys], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.board.positions.index(game.pieces[0][0].position) + assert_equal 1, game.board.positions.index(game.pieces[0][1].position) + assert_equal 1, game.board.positions.index(game.pieces[0][5].position) + assert_equal 84, game.deck.length + end + + def test_game_termination + game = Game.new(6) + game.set_testing_game! + assert_equal 0, game.moves_by_current_player + + game.apply_move!(Move.new(game.pieces[0][0], game.board.positions[0], game.board.positions[36]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[0].contains.include?(game.pieces[0][1]) + assert game.board.positions[0].contains.include?(game.pieces[0][2]) + assert game.board.positions[0].contains.include?(game.pieces[0][3]) + assert game.board.positions[0].contains.include?(game.pieces[0][4]) + assert game.board.positions[0].contains.include?(game.pieces[0][5]) + assert_equal 18, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][1], game.board.positions[0], game.board.positions[35]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[0].contains.include?(game.pieces[0][2]) + assert game.board.positions[0].contains.include?(game.pieces[0][3]) + assert game.board.positions[0].contains.include?(game.pieces[0][4]) + assert game.board.positions[0].contains.include?(game.pieces[0][5]) + assert_equal 19, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][2], game.board.positions[0], game.board.positions[34]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[34].contains.include?(game.pieces[0][2]) + assert game.board.positions[0].contains.include?(game.pieces[0][3]) + assert game.board.positions[0].contains.include?(game.pieces[0][4]) + assert game.board.positions[0].contains.include?(game.pieces[0][5]) + assert_equal 20, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][3], game.board.positions[0], game.board.positions[33]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[34].contains.include?(game.pieces[0][2]) + assert game.board.positions[33].contains.include?(game.pieces[0][3]) + assert game.board.positions[0].contains.include?(game.pieces[0][4]) + assert game.board.positions[0].contains.include?(game.pieces[0][5]) + assert_equal 21, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][4], game.board.positions[0], game.board.positions[32]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[34].contains.include?(game.pieces[0][2]) + assert game.board.positions[33].contains.include?(game.pieces[0][3]) + assert game.board.positions[32].contains.include?(game.pieces[0][4]) + assert game.board.positions[0].contains.include?(game.pieces[0][5]) + assert_equal 22, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][5], game.board.positions[0], game.board.positions[31]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[34].contains.include?(game.pieces[0][2]) + assert game.board.positions[33].contains.include?(game.pieces[0][3]) + assert game.board.positions[32].contains.include?(game.pieces[0][4]) + assert game.board.positions[31].contains.include?(game.pieces[0][5]) + assert_equal 23, game.possible_moves.length + + # Now start moving pices onto the boat + game.apply_move!(Move.new(game.pieces[0][5], game.board.positions[31], game.board.positions[37]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[34].contains.include?(game.pieces[0][2]) + assert game.board.positions[33].contains.include?(game.pieces[0][3]) + assert game.board.positions[32].contains.include?(game.pieces[0][4]) + assert game.board.positions[37].contains.include?(game.pieces[0][5]) + assert_equal 20, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][4], game.board.positions[32], game.board.positions[37]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[34].contains.include?(game.pieces[0][2]) + assert game.board.positions[33].contains.include?(game.pieces[0][3]) + assert game.board.positions[37].contains.include?(game.pieces[0][4]) + assert game.board.positions[37].contains.include?(game.pieces[0][5]) + assert_equal 17, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][3], game.board.positions[33], game.board.positions[37]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[34].contains.include?(game.pieces[0][2]) + assert game.board.positions[37].contains.include?(game.pieces[0][3]) + assert game.board.positions[37].contains.include?(game.pieces[0][4]) + assert game.board.positions[37].contains.include?(game.pieces[0][5]) + assert_equal 14, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][2], game.board.positions[34], game.board.positions[37]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[37].contains.include?(game.pieces[0][2]) + assert game.board.positions[37].contains.include?(game.pieces[0][3]) + assert game.board.positions[37].contains.include?(game.pieces[0][4]) + assert game.board.positions[37].contains.include?(game.pieces[0][5]) + assert_equal 11, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][1], game.board.positions[35], game.board.positions[37]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[37].contains.include?(game.pieces[0][1]) + assert game.board.positions[37].contains.include?(game.pieces[0][2]) + assert game.board.positions[37].contains.include?(game.pieces[0][3]) + assert game.board.positions[37].contains.include?(game.pieces[0][4]) + assert game.board.positions[37].contains.include?(game.pieces[0][5]) + assert_equal 8, game.possible_moves.length + + assert_raise(GameWonNotice) {game.apply_move!(Move.new(game.pieces[0][0], game.board.positions[36], game.board.positions[37]), 0, false)} + + end + + def test_retreats + game = Game.new(5,6,6) + game.set_testing_game! + + game.pieces.each do |player| + player.each do |piece| + assert_equal game.board.positions[0], piece.position + end + end + assert_equal [:cell, :gun, :keys, :dagger, :hat, :skull, :bottle, + :keys, :dagger, :skull, :hat, :gun, :bottle, + :dagger, :bottle, :keys, :gun, :hat, :skull, + :dagger, :skull, :bottle, :gun, :keys, :hat, + :hat, :dagger, :keys, :bottle, :gun, :skull, + :keys, :bottle, :skull, :dagger, :hat, :gun, :boat], + game.board.positions.collect {|p| p.symbol} + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} +# assert_equal [:bottle, :keys, :skull], game.players_cards[5].sort_by {|c| c.to_s} + assert_equal 5, game.players_cards.length + assert_equal 5, game.players.length + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle, :skull], + game.deck + assert_equal 0, game.moves_by_current_player + assert_equal 18, game.possible_moves.length + game.apply_move!(game.possible_moves[0]) + assert_equal [:hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.board.positions.index(game.pieces[0][0].position) + assert_equal 87, game.deck.length + + assert_equal 1, game.moves_by_current_player + assert_equal 12, game.possible_moves.length + game.apply_move!(game.possible_moves[3]) + assert_equal [:hat], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.board.positions.index(game.pieces[0][0].position) + assert_equal 5, game.board.positions.index(game.pieces[0][1].position) + assert_equal 87, game.deck.length + + # Test can't retreat into the cell + assert_equal 2, game.moves_by_current_player + assert_equal 7, game.possible_moves.length + assert_raise(InvalidMoveError) do + game.apply_move!(Move.new(game.pieces[0][1], game.board.positions[5], game.board.positions[0])) + end + end + + # Test invalid move error trapping + def test_invalid_move_error_trapping + game = Game.new(5,6,6) + game.set_testing_game! + + game.pieces.each do |player| + player.each do |piece| + assert_equal game.board.positions[0], piece.position + end + end + assert_equal [:cell, :gun, :keys, :dagger, :hat, :skull, :bottle, + :keys, :dagger, :skull, :hat, :gun, :bottle, + :dagger, :bottle, :keys, :gun, :hat, :skull, + :dagger, :skull, :bottle, :gun, :keys, :hat, + :hat, :dagger, :keys, :bottle, :gun, :skull, + :keys, :bottle, :skull, :dagger, :hat, :gun, :boat], + game.board.positions.collect {|p| p.symbol} + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal 5, game.players_cards.length + assert_equal 5, game.players.length + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle, :skull], + game.deck + assert_equal 0, game.moves_by_current_player + + # Test if can move to a space without the right card + assert_raise(InvalidMoveError) {game.apply_move!(Move.new(game.pieces[0][0], game.board.positions[2], game.board.positions[0]))} + + + # Apply a series of moves to get an interesting game state + game.apply_move!('0 0 5'.to_move(game)) + assert_equal [:gun, :hat], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 0 4'.to_move(game)) + assert_equal [:gun], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 5 4'.to_move(game)) + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 1, game.current_player + game.apply_move!('1 0 2'.to_move(game)) + assert_equal [:bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 0 7'.to_move(game)) + assert_equal [:bottle], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 7 4'.to_move(game)) + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 2, game.current_player + game.apply_move!('2 0 7'.to_move(game)) + assert_equal [:bottle, :hat], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 0 10'.to_move(game)) + assert_equal [:bottle], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 0 6'.to_move(game)) + assert_equal [], game.players_cards[2].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 3, game.current_player + game.apply_move!('3 0 12'.to_move(game)) + assert_equal [:skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 12 10'.to_move(game)) + assert_equal [:skull, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 0 5'.to_move(game)) + assert_equal [:skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 4, game.current_player + game.apply_move!('4 0 12'.to_move(game)) + assert_equal [:gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 12 10'.to_move(game)) + assert_equal [:dagger, :gun, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 0 3'.to_move(game)) + assert_equal [:gun, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:gun, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 0, game.current_player + + game.apply_move!('0 0 9'.to_move(game)) + assert_equal [:gun], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 0 1'.to_move(game)) + assert_equal [], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 9 7'.to_move(game)) + assert_equal [:keys], game.players_cards[0].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 1, game.current_player + game.apply_move!('1 0 12'.to_move(game)) + assert_equal [:bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 0 14'.to_move(game)) + assert_equal [:keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 12 7'.to_move(game)) + assert_equal [:hat, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 2, game.current_player + game.apply_move!('2 6 5'.to_move(game)) + assert_equal [:gun], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 0 11'.to_move(game)) + assert_equal [], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 11 5'.to_move(game)) + assert_equal [:bottle, :keys], game.players_cards[2].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 3, game.current_player + game.apply_move!('3 0 9'.to_move(game)) + assert_equal [:skull], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 0 18'.to_move(game)) + assert_equal [], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 18 14'.to_move(game)) + assert_equal [:bottle], game.players_cards[3].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 4, game.current_player + game.apply_move!('4 0 11'.to_move(game)) + assert_equal [:gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 0 16'.to_move(game)) + assert_equal [:gun], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 16 14'.to_move(game)) + assert_equal [:gun, :keys, :skull], game.players_cards[4].sort_by {|c| c.to_s} + + assert_equal [:keys], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:hat, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:gun, :keys, :skull], game.players_cards[4].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 0, game.current_player + + game.apply_move!('0 0 15'.to_move(game)) + assert_equal [], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 15 11'.to_move(game)) + assert_equal [:bottle], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 11 9'.to_move(game)) + assert_equal [:bottle, :skull], game.players_cards[0].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 1, game.current_player + game.apply_move!('1 0 15'.to_move(game)) + assert_equal [:hat, :keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 0 17'.to_move(game)) + assert_equal [:keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 17 15'.to_move(game)) + assert_equal [:keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 2, game.current_player + game.apply_move!('2 0 23'.to_move(game)) + assert_equal [:bottle], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 23 15'.to_move(game)) + assert_equal [:bottle, :dagger, :hat], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 0 17'.to_move(game)) + assert_equal [:bottle, :dagger], game.players_cards[2].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 3, game.current_player + game.apply_move!('3 10 9'.to_move(game)) + assert_equal [:bottle, :dagger, :hat], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 0 24'.to_move(game)) + assert_equal [:bottle, :dagger], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 0 8'.to_move(game)) + assert_equal [:bottle], game.players_cards[3].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 4, game.current_player + game.apply_move!('4 0 16'.to_move(game)) + assert_equal [:keys, :skull], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 16 11'.to_move(game)) + assert_equal [:keys, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 11 10'.to_move(game)) + assert_equal [:dagger, :dagger, :keys, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + + assert_equal 2, game.board.positions[ 0].contains.length + assert_equal 1, game.board.positions[ 1].contains.length + assert_equal 1, game.board.positions[ 2].contains.length + assert_equal 1, game.board.positions[ 3].contains.length + assert_equal 3, game.board.positions[ 4].contains.length + assert_equal 3, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 3, game.board.positions[ 7].contains.length + assert_equal 1, game.board.positions[ 8].contains.length + assert_equal 3, game.board.positions[ 9].contains.length + assert_equal 3, game.board.positions[10].contains.length + assert_equal 1, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 3, game.board.positions[14].contains.length + assert_equal 3, game.board.positions[15].contains.length + assert_equal 0, game.board.positions[16].contains.length + assert_equal 1, game.board.positions[17].contains.length + assert_equal 0, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 0, game.board.positions[20].contains.length + assert_equal 0, game.board.positions[21].contains.length + assert_equal 0, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 1, game.board.positions[24].contains.length + assert_equal 0, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 0, game.board.positions[27].contains.length + assert_equal 0, game.board.positions[28].contains.length + assert_equal 0, game.board.positions[29].contains.length + assert_equal 0, game.board.positions[30].contains.length + assert_equal 0, game.board.positions[31].contains.length + assert_equal 0, game.board.positions[32].contains.length + assert_equal 0, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 0, game.board.positions[35].contains.length + assert_equal 0, game.board.positions[36].contains.length + assert_equal 0, game.board.positions[37].contains.length + + assert_raise(InvalidMoveError) {game.apply_move!('0 7 6'.to_move(game))} # can't retreat into an empty space + assert_raise(InvalidMoveError) {game.apply_move!('0 7 5'.to_move(game))} # can't retreat into a space with three men + assert_raise(InvalidMoveError) {game.apply_move!('0 1 0'.to_move(game))} # can't retreat into the boat + assert_raise(InvalidMoveError) {game.apply_move!('0 1 12'.to_move(game))} # can't skip a vacant space of this type + assert_raise(InvalidMoveError) {game.apply_move!('0 1 5'.to_move(game))} # can't advance to a space with three pirates + assert_raise(InvalidMoveError) {game.apply_move!('0 2 6'.to_move(game))} # can't move another's piece + assert_raise(InvalidMoveError) {game.apply_move!('0 9 9'.to_move(game))} # can't advance the same square + assert_raise(InvalidMoveError) {game.apply_move!('0 9 20'.to_move(game))} # can't skip a vacant space of this type + + game.next_player! + assert_equal 0, game.current_player + + game.apply_move!('0 0 18'.to_move(game)) + assert_equal [:bottle], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 7 12'.to_move(game)) + assert_equal [], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 12 11'.to_move(game)) + assert_equal [:dagger], game.players_cards[0].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 1, game.current_player + game.apply_move!('1 14 11'.to_move(game)) + assert_equal [:dagger, :keys, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 2 23'.to_move(game)) + assert_equal [:dagger, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 15 14'.to_move(game)) + assert_equal [:bottle, :bottle, :dagger, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 2, game.current_player + game.apply_move!('2 17 15'.to_move(game)) + assert_equal [:bottle, :dagger, :dagger, :keys], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 5 27'.to_move(game)) + assert_equal [:bottle, :dagger, :dagger], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 5 13'.to_move(game)) + assert_equal [:bottle, :dagger], game.players_cards[2].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 3, game.current_player + game.apply_move!('3 8 7'.to_move(game)) + assert_equal [:bottle, :bottle, :hat], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 5 17'.to_move(game)) + assert_equal [:bottle, :bottle], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 7 12'.to_move(game)) + assert_equal [:bottle], game.players_cards[3].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 4, game.current_player + game.apply_move!('4 0 8'.to_move(game)) + assert_equal [:dagger, :keys, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 8 7'.to_move(game)) + assert_equal [:bottle, :dagger, :keys, :keys, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 3 31'.to_move(game)) + assert_equal [:bottle, :dagger, :keys, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + + + game.next_player! + assert_equal 0, game.current_player + + game.apply_move!('0 18 17'.to_move(game)) + assert_equal [:dagger, :gun], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 1 16'.to_move(game)) + assert_equal [:dagger], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 17 16'.to_move(game)) + assert_equal [:dagger, :skull], game.players_cards[0].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 1, game.current_player + game.apply_move!('1 4 37 keys'.to_move(game)) + assert_equal [:bottle, :bottle, :dagger, :keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 7 4'.to_move(game)) + assert_equal [:bottle, :bottle, :bottle, :dagger, :hat, :keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 4 37 keys'.to_move(game)) + assert_equal [:bottle, :bottle, :bottle, :dagger, :hat], game.players_cards[1].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 2, game.current_player + game.apply_move!('2 7 21'.to_move(game)) + assert_equal [:dagger], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 13 12'.to_move(game)) + assert_equal [:dagger, :dagger], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 10 13'.to_move(game)) + assert_equal [:dagger], game.players_cards[2].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 3, game.current_player + game.apply_move!('3 17 16'.to_move(game)) + assert_equal [:bottle, :hat, :skull], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 9 28'.to_move(game)) + assert_equal [:hat, :skull], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 9 18'.to_move(game)) + assert_equal [:hat], game.players_cards[3].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 4, game.current_player + game.apply_move!('4 7 37 keys'.to_move(game)) + assert_equal [:bottle, :dagger, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 11 10'.to_move(game)) + assert_equal [:bottle, :dagger, :dagger, :gun, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 10 32'.to_move(game)) + assert_equal [:dagger, :dagger, :gun, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + + + game.next_player! + assert_equal 0, game.current_player + + game.apply_move!('0 11 10'.to_move(game)) + assert_equal [:bottle, :dagger, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 4 8'.to_move(game)) + assert_equal [:bottle, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 4 17'.to_move(game)) + assert_equal [:bottle, :skull], game.players_cards[0].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 1, game.current_player + game.apply_move!('1 11 37 bottle'.to_move(game)) + assert_equal [:bottle, :bottle, :dagger, :hat], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 14 37 bottle'.to_move(game)) + assert_equal [:bottle, :dagger, :hat], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 15 37 bottle'.to_move(game)) + assert_equal [:dagger, :hat], game.players_cards[1].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 2, game.current_player + game.apply_move!('2 13 12'.to_move(game)) + assert_equal [:dagger, :gun, :hat], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 12 22 gun'.to_move(game)) + assert_equal [:dagger, :hat], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 12 25 hat'.to_move(game)) + assert_equal [:dagger], game.players_cards[2].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 3, game.current_player + game.apply_move!('3 16 15'.to_move(game)) + assert_equal [:dagger, :gun, :hat], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 12 35 hat'.to_move(game)) + assert_equal [:dagger, :gun], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 14 29 gun'.to_move(game)) + assert_equal [:dagger], game.players_cards[3].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 4, game.current_player + game.apply_move!('4 10 20 skull'.to_move(game)) + assert_equal [:dagger, :dagger, :gun, :skull], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 10 30 skull'.to_move(game)) + assert_equal [:dagger, :dagger, :gun], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 14 36 gun'.to_move(game)) + assert_equal [:dagger, :dagger], game.players_cards[4].sort_by {|c| c.to_s} + + + game.next_player! + assert_equal 0, game.current_player + + game.apply_move!('0 17 16'.to_move(game)) + assert_equal [:bottle, :gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 8 33 skull'.to_move(game)) + assert_equal [:bottle, :gun, :hat], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 9 17 hat'.to_move(game)) + assert_equal [:bottle, :gun], game.players_cards[0].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 1, game.current_player + assert_raise(GameWonNotice) {game.apply_move!('1 23 37 hat'.to_move(game))} + assert_equal [:dagger], game.players_cards[1].sort_by {|c| c.to_s} + + assert_equal 0, game.board.positions[ 0].contains.length + assert_equal 0, game.board.positions[ 1].contains.length + assert_equal 0, game.board.positions[ 2].contains.length + assert_equal 0, game.board.positions[ 3].contains.length + assert_equal 0, game.board.positions[ 4].contains.length + assert_equal 0, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 0, game.board.positions[ 7].contains.length + assert_equal 0, game.board.positions[ 8].contains.length + assert_equal 0, game.board.positions[ 9].contains.length + assert_equal 1, game.board.positions[10].contains.length + assert_equal 0, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 0, game.board.positions[14].contains.length + assert_equal 3, game.board.positions[15].contains.length + assert_equal 3, game.board.positions[16].contains.length + assert_equal 1, game.board.positions[17].contains.length + assert_equal 1, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 1, game.board.positions[20].contains.length + assert_equal 1, game.board.positions[21].contains.length + assert_equal 1, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 1, game.board.positions[24].contains.length + assert_equal 1, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 1, game.board.positions[27].contains.length + assert_equal 1, game.board.positions[28].contains.length + assert_equal 1, game.board.positions[29].contains.length + assert_equal 1, game.board.positions[30].contains.length + assert_equal 1, game.board.positions[31].contains.length + assert_equal 1, game.board.positions[32].contains.length + assert_equal 1, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 1, game.board.positions[35].contains.length + assert_equal 1, game.board.positions[36].contains.length + assert_equal 7, game.board.positions[37].contains.length + + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun], + game.deck + + assert_equal [:bottle, :gun], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:dagger], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:dagger], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:dagger], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:dagger, :dagger], game.players_cards[4].sort_by {|c| c.to_s} + + end + # + # : move without a card + # move to an occupied space + # retreat to an emtpy space + # retreat to an overfull space + # retreat to the cell with one pirate already in it + + + # Test current player switching + + # Test undo moves + + # Test file reading + # (need a full game file to read) + + def test_undo + game = Game.new(5,6,6) + game.set_testing_game! + game.pieces.each do |player| + player.each do |piece| + assert_equal game.board.positions[0], piece.position + end + end + assert_equal [:cell, :gun, :keys, :dagger, :hat, :skull, :bottle, + :keys, :dagger, :skull, :hat, :gun, :bottle, + :dagger, :bottle, :keys, :gun, :hat, :skull, + :dagger, :skull, :bottle, :gun, :keys, :hat, + :hat, :dagger, :keys, :bottle, :gun, :skull, + :keys, :bottle, :skull, :dagger, :hat, :gun, :boat], + game.board.positions.collect {|p| p.symbol} + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal 5, game.players_cards.length + assert_equal 5, game.players.length + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle, :skull], + game.deck + + assert_equal 0, game.history.length + + game.apply_move!('0 0 5'.to_move(game)) + assert_equal [:gun, :hat], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.history.length + game.apply_move!('0 0 4'.to_move(game)) + assert_equal [:gun], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 2, game.history.length + game.apply_move!('0 5 4'.to_move(game)) + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 3, game.history.length + + game.next_player! + assert_equal 1, game.current_player + assert_equal 3, game.history.length + game.apply_move!('1 0 2'.to_move(game)) + assert_equal [:bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal 4, game.history.length + game.apply_move!('1 0 7'.to_move(game)) + assert_equal [:bottle], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal 5, game.history.length + game.apply_move!('1 7 4'.to_move(game)) + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal 6, game.history.length + assert_equal 1, game.current_player + assert_equal 3, game.moves_by_current_player + + assert_equal 26, game.board.positions[ 0].contains.length + assert_equal 0, game.board.positions[ 1].contains.length + assert_equal 1, game.board.positions[ 2].contains.length + assert_equal 0, game.board.positions[ 3].contains.length + assert_equal 3, game.board.positions[ 4].contains.length + assert_equal 0, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 0, game.board.positions[ 7].contains.length + assert_equal 0, game.board.positions[ 8].contains.length + assert_equal 0, game.board.positions[ 9].contains.length + assert_equal 0, game.board.positions[10].contains.length + assert_equal 0, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 0, game.board.positions[14].contains.length + assert_equal 0, game.board.positions[15].contains.length + assert_equal 0, game.board.positions[16].contains.length + assert_equal 0, game.board.positions[17].contains.length + assert_equal 0, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 0, game.board.positions[20].contains.length + assert_equal 0, game.board.positions[21].contains.length + assert_equal 0, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 0, game.board.positions[24].contains.length + assert_equal 0, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 0, game.board.positions[27].contains.length + assert_equal 0, game.board.positions[28].contains.length + assert_equal 0, game.board.positions[29].contains.length + assert_equal 0, game.board.positions[30].contains.length + assert_equal 0, game.board.positions[31].contains.length + assert_equal 0, game.board.positions[32].contains.length + assert_equal 0, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 0, game.board.positions[35].contains.length + assert_equal 0, game.board.positions[36].contains.length + assert_equal 0, game.board.positions[37].contains.length + + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull], + game.deck + + game.undo_move! # player 1's third move + + assert_equal 5, game.history.length + + assert_equal 26, game.board.positions[ 0].contains.length + assert_equal 0, game.board.positions[ 1].contains.length + assert_equal 1, game.board.positions[ 2].contains.length + assert_equal 0, game.board.positions[ 3].contains.length + assert_equal 2, game.board.positions[ 4].contains.length + assert_equal 0, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 1, game.board.positions[ 7].contains.length + assert_equal 0, game.board.positions[ 8].contains.length + assert_equal 0, game.board.positions[ 9].contains.length + assert_equal 0, game.board.positions[10].contains.length + assert_equal 0, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 0, game.board.positions[14].contains.length + assert_equal 0, game.board.positions[15].contains.length + assert_equal 0, game.board.positions[16].contains.length + assert_equal 0, game.board.positions[17].contains.length + assert_equal 0, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 0, game.board.positions[20].contains.length + assert_equal 0, game.board.positions[21].contains.length + assert_equal 0, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 0, game.board.positions[24].contains.length + assert_equal 0, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 0, game.board.positions[27].contains.length + assert_equal 0, game.board.positions[28].contains.length + assert_equal 0, game.board.positions[29].contains.length + assert_equal 0, game.board.positions[30].contains.length + assert_equal 0, game.board.positions[31].contains.length + assert_equal 0, game.board.positions[32].contains.length + assert_equal 0, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 0, game.board.positions[35].contains.length + assert_equal 0, game.board.positions[36].contains.length + assert_equal 0, game.board.positions[37].contains.length + + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle], + game.deck + assert_equal 1, game.current_player + assert_equal 2, game.moves_by_current_player + + # Re-apply player 1's third move + game.apply_move!('1 7 4'.to_move(game)) + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 2, game.current_player + game.apply_move!('2 0 7'.to_move(game)) + assert_equal [:bottle, :hat], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 0 10'.to_move(game)) + assert_equal [:bottle], game.players_cards[2].sort_by {|c| c.to_s} + + assert_equal 8, game.history.length + + assert_equal 24, game.board.positions[ 0].contains.length + assert_equal 0, game.board.positions[ 1].contains.length + assert_equal 1, game.board.positions[ 2].contains.length + assert_equal 0, game.board.positions[ 3].contains.length + assert_equal 3, game.board.positions[ 4].contains.length + assert_equal 0, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 1, game.board.positions[ 7].contains.length + assert_equal 0, game.board.positions[ 8].contains.length + assert_equal 0, game.board.positions[ 9].contains.length + assert_equal 1, game.board.positions[10].contains.length + assert_equal 0, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 0, game.board.positions[14].contains.length + assert_equal 0, game.board.positions[15].contains.length + assert_equal 0, game.board.positions[16].contains.length + assert_equal 0, game.board.positions[17].contains.length + assert_equal 0, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 0, game.board.positions[20].contains.length + assert_equal 0, game.board.positions[21].contains.length + assert_equal 0, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 0, game.board.positions[24].contains.length + assert_equal 0, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 0, game.board.positions[27].contains.length + assert_equal 0, game.board.positions[28].contains.length + assert_equal 0, game.board.positions[29].contains.length + assert_equal 0, game.board.positions[30].contains.length + assert_equal 0, game.board.positions[31].contains.length + assert_equal 0, game.board.positions[32].contains.length + assert_equal 0, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 0, game.board.positions[35].contains.length + assert_equal 0, game.board.positions[36].contains.length + assert_equal 0, game.board.positions[37].contains.length + + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull], + game.deck + assert_equal 2, game.current_player + assert_equal 2, game.moves_by_current_player + + game.undo_move! # player 2's second move + + assert_equal 7, game.history.length + + assert_equal 25, game.board.positions[ 0].contains.length + assert_equal 0, game.board.positions[ 1].contains.length + assert_equal 1, game.board.positions[ 2].contains.length + assert_equal 0, game.board.positions[ 3].contains.length + assert_equal 3, game.board.positions[ 4].contains.length + assert_equal 0, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 1, game.board.positions[ 7].contains.length + assert_equal 0, game.board.positions[ 8].contains.length + assert_equal 0, game.board.positions[ 9].contains.length + assert_equal 0, game.board.positions[10].contains.length + assert_equal 0, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 0, game.board.positions[14].contains.length + assert_equal 0, game.board.positions[15].contains.length + assert_equal 0, game.board.positions[16].contains.length + assert_equal 0, game.board.positions[17].contains.length + assert_equal 0, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 0, game.board.positions[20].contains.length + assert_equal 0, game.board.positions[21].contains.length + assert_equal 0, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 0, game.board.positions[24].contains.length + assert_equal 0, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 0, game.board.positions[27].contains.length + assert_equal 0, game.board.positions[28].contains.length + assert_equal 0, game.board.positions[29].contains.length + assert_equal 0, game.board.positions[30].contains.length + assert_equal 0, game.board.positions[31].contains.length + assert_equal 0, game.board.positions[32].contains.length + assert_equal 0, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 0, game.board.positions[35].contains.length + assert_equal 0, game.board.positions[36].contains.length + assert_equal 0, game.board.positions[37].contains.length + + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull], + game.deck + assert_equal 2, game.current_player + assert_equal 1, game.moves_by_current_player + + + game.undo_move! # Player 2's first move + + assert_equal 6, game.history.length + + assert_equal 26, game.board.positions[ 0].contains.length + assert_equal 0, game.board.positions[ 1].contains.length + assert_equal 1, game.board.positions[ 2].contains.length + assert_equal 0, game.board.positions[ 3].contains.length + assert_equal 3, game.board.positions[ 4].contains.length + assert_equal 0, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 0, game.board.positions[ 7].contains.length + assert_equal 0, game.board.positions[ 8].contains.length + assert_equal 0, game.board.positions[ 9].contains.length + assert_equal 0, game.board.positions[10].contains.length + assert_equal 0, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 0, game.board.positions[14].contains.length + assert_equal 0, game.board.positions[15].contains.length + assert_equal 0, game.board.positions[16].contains.length + assert_equal 0, game.board.positions[17].contains.length + assert_equal 0, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 0, game.board.positions[20].contains.length + assert_equal 0, game.board.positions[21].contains.length + assert_equal 0, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 0, game.board.positions[24].contains.length + assert_equal 0, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 0, game.board.positions[27].contains.length + assert_equal 0, game.board.positions[28].contains.length + assert_equal 0, game.board.positions[29].contains.length + assert_equal 0, game.board.positions[30].contains.length + assert_equal 0, game.board.positions[31].contains.length + assert_equal 0, game.board.positions[32].contains.length + assert_equal 0, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 0, game.board.positions[35].contains.length + assert_equal 0, game.board.positions[36].contains.length + assert_equal 0, game.board.positions[37].contains.length + + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull], + game.deck + assert_equal 2, game.current_player + assert_equal 0, game.moves_by_current_player + + + game.undo_move! # Player 1's third move + + assert_equal 5, game.history.length + + assert_equal 26, game.board.positions[ 0].contains.length + assert_equal 0, game.board.positions[ 1].contains.length + assert_equal 1, game.board.positions[ 2].contains.length + assert_equal 0, game.board.positions[ 3].contains.length + assert_equal 2, game.board.positions[ 4].contains.length + assert_equal 0, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 1, game.board.positions[ 7].contains.length + assert_equal 0, game.board.positions[ 8].contains.length + assert_equal 0, game.board.positions[ 9].contains.length + assert_equal 0, game.board.positions[10].contains.length + assert_equal 0, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 0, game.board.positions[14].contains.length + assert_equal 0, game.board.positions[15].contains.length + assert_equal 0, game.board.positions[16].contains.length + assert_equal 0, game.board.positions[17].contains.length + assert_equal 0, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 0, game.board.positions[20].contains.length + assert_equal 0, game.board.positions[21].contains.length + assert_equal 0, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 0, game.board.positions[24].contains.length + assert_equal 0, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 0, game.board.positions[27].contains.length + assert_equal 0, game.board.positions[28].contains.length + assert_equal 0, game.board.positions[29].contains.length + assert_equal 0, game.board.positions[30].contains.length + assert_equal 0, game.board.positions[31].contains.length + assert_equal 0, game.board.positions[32].contains.length + assert_equal 0, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 0, game.board.positions[35].contains.length + assert_equal 0, game.board.positions[36].contains.length + assert_equal 0, game.board.positions[37].contains.length + + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle], + game.deck + assert_equal 1, game.current_player + assert_equal 2, game.moves_by_current_player + + end + + def test_apply_moves + game = Game.new(5,6,6) + game.set_testing_game! + game.pieces.each do |player| + player.each do |piece| + assert_equal game.board.positions[0], piece.position + end + end + assert_equal [:cell, :gun, :keys, :dagger, :hat, :skull, :bottle, + :keys, :dagger, :skull, :hat, :gun, :bottle, + :dagger, :bottle, :keys, :gun, :hat, :skull, + :dagger, :skull, :bottle, :gun, :keys, :hat, + :hat, :dagger, :keys, :bottle, :gun, :skull, + :keys, :bottle, :skull, :dagger, :hat, :gun, :boat], + game.board.positions.collect {|p| p.symbol} + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal 5, game.players_cards.length + assert_equal 5, game.players.length + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle, :skull], + game.deck + + assert_equal 0, game.history.length + + game.apply_move!('0 0 5'.to_move(game)) + assert_equal [:gun, :hat], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.history.length + game.apply_move!('0 0 4'.to_move(game)) + assert_equal [:gun], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 2, game.history.length + game.apply_move!('0 5 4'.to_move(game)) + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 3, game.history.length + + game.next_player! + assert_equal 1, game.current_player + assert_equal 3, game.history.length + game.apply_move!('1 0 2'.to_move(game)) + end + + def test_read_game_lines + gamelines1 = ["5", "cell", "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 5 skull", "1 0 4 hat", + "1", + "dagger"] + assert_nothing_raised {Game.read_game(gamelines1)} + game, moves = Game.read_game(gamelines1) + assert_equal 2, game.history.length + assert_equal 0, game.current_player + assert_equal 2, game.moves_by_current_player + assert_equal 1, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 15, game.deck.length + + game.apply_move!('0 5 4'.to_move(game)) + assert_equal 3, game.history.length + assert_equal 0, game.current_player + assert_equal 3, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 16, game.deck.length + + gamelines2 = ["5", "cell", "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 5", "1 0 4", "1 5 4", + "2", + "dagger", "hat", "keys"] + assert_nothing_raised {Game.read_game(gamelines2)} + game, moves = Game.read_game(gamelines2) + assert_equal 3, game.history.length + assert_equal 1, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 16, game.deck.length + + game.apply_move!('1 0 2'.to_move(game)) + assert_equal 4, game.history.length + assert_equal 1, game.current_player + assert_equal 1, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 2, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 16, game.deck.length + + gamelines3 = ["5", "cell", "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 5", "1 0 4", "1 5 4", "2 0 2", + "2", + "dagger", "keys"] + assert_nothing_raised {Game.read_game(gamelines3)} + game, moves = Game.read_game(gamelines3) + assert_equal 4, game.history.length + assert_equal 1, game.current_player + assert_equal 1, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 2, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 16, game.deck.length + + game.apply_move!('1 0 7'.to_move(game)) + assert_equal 5, game.history.length + assert_equal 1, game.current_player + assert_equal 2, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 1, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 16, game.deck.length + + gamelines4 = ["5", "cell", "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 5", "1 0 4", "1 5 4", "2 0 2", "2 0 7", + "2", + "dagger"] + assert_nothing_raised {Game.read_game(gamelines4)} + game, moves = Game.read_game(gamelines4) + assert_equal 5, game.history.length + assert_equal 1, game.current_player + assert_equal 2, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 1, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 16, game.deck.length + + game.apply_move!('1 7 4'.to_move(game)) + assert_equal 6, game.history.length + assert_equal 1, game.current_player + assert_equal 3, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 18, game.deck.length + + gamelines5 = ["5", "cell", "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 5", "1 0 4", "1 5 4", "2 0 2", "2 0 7", "2 7 4", + "2", + "bottle", "hat", "keys"] + assert_nothing_raised {Game.read_game(gamelines5)} + game, moves = Game.read_game(gamelines5) + assert_equal 6, game.history.length + assert_equal 1, game.current_player + assert_equal 3, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 18, game.deck.length + + game.players_cards[2] << :keys + game.players_cards[2] << :hat + game.apply_move!('3 0 7'.to_move(game, true), 2) + assert_equal 7, game.history.length + assert_equal 2, game.current_player + assert_equal 1, game.moves_by_current_player + game.apply_move!('3 0 10'.to_move(game, true)) + assert_equal 8, game.history.length + assert_equal 2, game.current_player + assert_equal 2, game.moves_by_current_player + + gamelines6 = ["5", "cell", "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 5", "1 0 4", "1 5 4", "2 0 2", "2 0 7", "2 7 4", + "3", + "bottle", "hat", "keys"] + assert_nothing_raised {Game.read_game(gamelines6)} + game, moves = Game.read_game(gamelines6) + assert_equal 6, game.history.length + assert_equal 2, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 18, game.deck.length + + game.apply_move!('2 0 7'.to_move(game)) + assert_equal 7, game.history.length + assert_equal 2, game.current_player + assert_equal 1, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 2, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 18, game.deck.length + + game.apply_move!('2 0 10'.to_move(game)) + assert_equal 8, game.history.length + assert_equal 2, game.current_player + assert_equal 2, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 1, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 18, game.deck.length + + # Need to do a test with a very long sequence of moves, + # to test that the card dealing works properly when the deck is exhausted + + end + + def test_dealing + game = Game.new(5,6,6) + game.set_testing_game! + 0.upto(4) do |i| + assert_equal $INITIAL_CARDS_PER_PLAYER, game.players_cards[i].length + end + assert_equal $SYMBOLS.length * $CARDS_PER_SYMBOL - 5 * $INITIAL_CARDS_PER_PLAYER, game.deck.length + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal 5, game.players_cards.length + assert_equal 5, game.players.length + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle, :skull], + game.deck + game.deal_cards!(86, 0) + assert_equal 89, game.players_cards[0].length + assert_equal 1, game.deck.length + assert_equal [:hat], game.deck + + game.players_cards[0] = [:hat, :hat, :hat, :hat, :hat] + game.players_cards[1] = [:keys, :keys, :keys] + game.players_cards[2] = [:bottle, :dagger, :skull] + game.players_cards[3] = [] + game.players_cards[4] = [] + game.deal_cards!(1, 3) + + assert_equal [:hat, :hat, :hat, :hat, :hat], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:keys, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :dagger, :skull], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:hat], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal [], game.deck + + game.deal_cards!(1, 4) + assert_equal [:keys], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal $CARDS_PER_SYMBOL - 6, game.deck.select {|c| c == :hat}.length + assert_equal $CARDS_PER_SYMBOL - 4, game.deck.select {|c| c == :keys}.length + assert_equal $CARDS_PER_SYMBOL - 1, game.deck.select {|c| c == :bottle}.length + assert_equal $CARDS_PER_SYMBOL - 1, game.deck.select {|c| c == :dagger}.length + assert_equal $CARDS_PER_SYMBOL - 1, game.deck.select {|c| c == :skull}.length + assert_equal $CARDS_PER_SYMBOL, game.deck.select {|c| c == :gun}.length + assert_equal $CARDS_PER_SYMBOL * 6 - 13, game.deck.length + + end + + def test_long_deal + gamelines0 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1", + "hat", "skull", "bottle"] + assert_nothing_raised {Game.read_game(gamelines0)} + game, moves = Game.read_game(gamelines0) + assert_equal 0, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 3, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 9, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 9, game.deck.length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :skull}).length + + gamelines1 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1 gun", "1 0 2 keys", "1 2 1", + "2", + "hat", "skull", "bottle"] + assert_nothing_raised {Game.read_game(gamelines1)} + game, moves = Game.read_game(gamelines1) + assert_equal 3, game.history.length + assert_equal 1, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 10, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 2, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 10, game.deck.length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 1, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 1, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :skull}).length + + gamelines2 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1", + "hat"] + assert_nothing_raised {Game.read_game(gamelines2)} + game, moves = Game.read_game(gamelines2) + assert_equal 18, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 1, game.players_cards[0].length + assert_equal 1, game.players_cards[1].length + assert_equal 1, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 6), game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 12, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 6), game.deck.length + assert_equal $CARDS_PER_SYMBOL - 2, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 2, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 2, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 2, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 2, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 2, (unplayed_cards.select {|c| c == :skull}).length + + gamelines2 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1", + "hat", "skull", "bottle"] + assert_nothing_raised {Game.read_game(gamelines2)} + game, moves = Game.read_game(gamelines2) + assert_equal 30, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 3, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 18), game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 18, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 18), game.deck.length + assert_equal $CARDS_PER_SYMBOL - 3, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 3, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 3, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 3, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 3, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 3, (unplayed_cards.select {|c| c == :skull}).length + + gamelines3 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1", + "hat", "skull", "bottle", "gun", "dagger"] + assert_nothing_raised {Game.read_game(gamelines3)} + game, moves = Game.read_game(gamelines3) + assert_equal 42, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 5, game.players_cards[0].length + assert_equal 5, game.players_cards[1].length + assert_equal 5, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 30), game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 24, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 30), game.deck.length + assert_equal $CARDS_PER_SYMBOL - 4, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 4, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 4, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 4, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 4, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 4, (unplayed_cards.select {|c| c == :skull}).length + + gamelines4 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1", + "hat", "skull", "bottle", "gun", "dagger", "keys", "hat"] + assert_nothing_raised {Game.read_game(gamelines4)} + game, moves = Game.read_game(gamelines4) + assert_equal 54, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 7, game.players_cards[0].length + assert_equal 7, game.players_cards[1].length + assert_equal 7, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 42), game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 30, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 42), game.deck.length + assert_equal $CARDS_PER_SYMBOL - 5, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 5, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 5, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 5, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 5, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 5, (unplayed_cards.select {|c| c == :skull}).length + + + gamelines5 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1", + "hat", "skull", "bottle", "gun", "dagger", "keys", "hat", "skull", "bottle", "gun", "dagger"] + assert_nothing_raised {Game.read_game(gamelines5)} + game, moves = Game.read_game(gamelines5) + assert_equal 78, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 11, game.players_cards[0].length + assert_equal 11, game.players_cards[1].length + assert_equal 11, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 66), game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 42, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 66), game.deck.length + assert_equal $CARDS_PER_SYMBOL - 7, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 7, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 7, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 7, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 7, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 7, (unplayed_cards.select {|c| c == :skull}).length + + gamelines6 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle"] + assert_nothing_raised {Game.read_game(gamelines6)} + game, moves = Game.read_game(gamelines6) + assert_equal 102, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 15, game.players_cards[0].length + assert_equal 15, game.players_cards[1].length + assert_equal 15, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 90), game.deck.length + assert_equal 3, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 54, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 90), game.deck.length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :skull}).length + + gamelines7 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", + "1", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle", "gun"] + assert_nothing_raised {Game.read_game(gamelines7)} + game, moves = Game.read_game(gamelines7) + assert_equal 104, game.history.length + assert_equal 0, game.current_player + assert_equal 2, game.moves_by_current_player + assert_equal 16, game.players_cards[0].length + assert_equal 15, game.players_cards[1].length + assert_equal 15, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 55, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :skull}).length + + gamelines8 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "1", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "skull", "bottle", "dagger"] + assert_nothing_raised {Game.read_game(gamelines8)} + game, moves = Game.read_game(gamelines8) + assert_equal 105, game.history.length + assert_equal 0, game.current_player + assert_equal 3, game.moves_by_current_player + assert_equal 15, game.players_cards[0].length + assert_equal 15, game.players_cards[1].length + assert_equal 15, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 56, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :skull}).length + + + gamelines9 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 12", "2 12 10", + "2", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle"] + assert_nothing_raised {Game.read_game(gamelines9)} + game, moves = Game.read_game(gamelines9) + assert_equal 107, game.history.length + assert_equal 1, game.current_player + assert_equal 2, game.moves_by_current_player + assert_equal 15, game.players_cards[0].length + assert_equal 15, game.players_cards[1].length + assert_equal 15, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 45, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 0, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :skull}).length + + gamelines10 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "1", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "hat", "hat", "hat", "hat", "hat", + "hat", "hat", "hat"] + assert_raise(InvalidMoveError) {Game.read_game(gamelines10)} # Not enough skull cards to go round + + gamelines11 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "skull", "bottle", "dagger"] + assert_nothing_raised {Game.read_game(gamelines11)} + game, moves = Game.read_game(gamelines11) + assert_equal 111, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 15, game.players_cards[0].length + assert_equal 12, game.players_cards[1].length + assert_equal 12, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 62, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :skull}).length + + gamelines12 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle", "gun", "dagger", "keys"] + assert_nothing_raised {Game.read_game(gamelines12)} + game, moves = Game.read_game(gamelines12) + assert_equal 114, game.history.length + assert_equal 1, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 12, game.players_cards[0].length + assert_equal 12, game.players_cards[1].length + assert_equal 12, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 65, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 14, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :skull}).length + + gamelines13 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3", + "skull", "skull", "bottle", "gun", "dagger", "keys", + "bottle", "skull", "bottle", "gun", "dagger", "keys"] + assert_nothing_raised {Game.read_game(gamelines13)} + game, moves = Game.read_game(gamelines13) + assert_equal 117, game.history.length + assert_equal 2, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 12, game.players_cards[0].length + assert_equal 9, game.players_cards[1].length + assert_equal 12, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 68, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :skull}).length + + gamelines14 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 37", + "3", + "skull", "skull", "bottle", "gun", "dagger", "keys", + "bottle", "skull", "bottle", "gun", "dagger"] + assert_raise(InvalidMoveError) {Game.read_game(gamelines14)} + + gamelines15 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 12 bottle", "3 6 21 bottle", "3 6 28 bottle", + "1", + "skull", "skull", "bottle", "gun", "dagger", "keys", + "bottle", "skull", "bottle", "gun", "dagger", "keys"] + assert_nothing_raised {Game.read_game(gamelines15)} + game, moves = Game.read_game(gamelines15) + assert_equal 120, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 12, game.players_cards[0].length + assert_equal 9, game.players_cards[1].length + assert_equal 9, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 71, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 13, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :skull}).length + + gamelines16 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 12 bottle", "3 6 21 bottle", "3 6 28 bottle", + "1 4 32 bottle", "1 6 37 bottle", "1 10 37 bottle", + "2", + "skull", "skull", "keys", "gun", "dagger", "keys", + "skull", "skull", "skull"] + assert_nothing_raised {Game.read_game(gamelines16)} + game, moves = Game.read_game(gamelines16) + assert_equal 123, game.history.length + assert_equal 1, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 9, game.players_cards[0].length + assert_equal 9, game.players_cards[1].length + assert_equal 9, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 74, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 16, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :skull}).length + + gamelines17 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 12 bottle", "3 6 21 bottle", "3 6 28 bottle", + "1 4 32 bottle", "1 6 37 bottle", "1 10 37 bottle", + "2 13 37 bottle", "2 14 23 keys", "2 15 27 keys", + "3", + "skull", "skull", "keys", "gun", "dagger", "keys", + "skull", "skull", "skull"] + assert_nothing_raised {Game.read_game(gamelines17)} + game, moves = Game.read_game(gamelines17) + assert_equal 126, game.history.length + assert_equal 2, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 9, game.players_cards[0].length + assert_equal 6, game.players_cards[1].length + assert_equal 9, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 77, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 13, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :skull}).length + + gamelines18 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 12 bottle", "3 6 21 bottle", "3 6 28 bottle", + "1 4 32 bottle", "1 6 37 bottle", "1 10 37 bottle", + "2 13 37 bottle", "2 14 23 keys", "2 15 27 keys", + "3 12 20 skull", "3 16 31 keys", "3 17 37 keys", + "1", + "skull", "skull", "keys", "gun", "dagger", "keys", + "skull", "skull", "skull"] + assert_nothing_raised {Game.read_game(gamelines18)} + game, moves = Game.read_game(gamelines18) + assert_equal 129, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 9, game.players_cards[0].length + assert_equal 6, game.players_cards[1].length + assert_equal 6, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 80, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 15, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :skull}).length + + gamelines19 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 12 bottle", "3 6 21 bottle", "3 6 28 bottle", + "1 4 32 bottle", "1 6 37 bottle", "1 10 37 bottle", + "2 13 37 bottle", "2 14 23 keys", "2 15 27 keys", + "3 12 20 skull", "3 16 31 keys", "3 17 37 keys", + "1 24 37 keys", "1 25 37 keys", "1 32 36 gun", + "2", + "skull", "skull", "gun", "gun", "dagger", "gun"] + assert_nothing_raised {Game.read_game(gamelines19)} + game, moves = Game.read_game(gamelines19) + assert_equal 132, game.history.length + assert_equal 1, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 6, game.players_cards[0].length + assert_equal 6, game.players_cards[1].length + assert_equal 6, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 83, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :skull}).length + + gamelines20 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 12 bottle", "3 6 21 bottle", "3 6 28 bottle", + "1 4 32 bottle", "1 6 37 bottle", "1 10 37 bottle", + "2 13 37 bottle", "2 14 23 keys", "2 15 27 keys", + "3 12 20 skull", "3 16 31 keys", "3 17 37 keys", + "1 24 37 keys", "1 25 37 keys", "1 32 36 gun", + "2 23 30 skull", "2 27 33 skull", "2 30 37 skull", + "3", + "skull", "skull", "gun", "gun", "dagger", "gun"] + assert_nothing_raised {Game.read_game(gamelines20)} + game, moves = Game.read_game(gamelines20) + assert_equal 135, game.history.length + assert_equal 2, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 6, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 6, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 86, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 14, (unplayed_cards.select {|c| c == :skull}).length + + gamelines21 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 12 bottle", "3 6 21 bottle", "3 6 28 bottle", + "1 4 32 bottle", "1 6 37 bottle", "1 10 37 bottle", + "2 13 37 bottle", "2 14 23 keys", "2 15 27 keys", + "3 12 20 skull", "3 16 31 keys", "3 17 37 keys", + "1 24 37 keys", "1 25 37 keys", "1 32 36 gun", + "2 23 30 skull", "2 27 33 skull", "2 30 37 skull", + "3 18 30 skull", "3 20 37 skull", "3 21 37 skull", + "1", + "gun", "gun", "gun", "gun", "gun", "gun"] + assert_nothing_raised {Game.read_game(gamelines21)} + game, moves = Game.read_game(gamelines21) + assert_equal 138, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 6, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 89, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :skull}).length + + gamelines22 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 12 bottle", "3 6 21 bottle", "3 6 28 bottle", + "1 4 32 bottle", "1 6 37 bottle", "1 10 37 bottle", + "2 13 37 bottle", "2 14 23 keys", "2 15 27 keys", + "3 12 20 skull", "3 16 31 keys", "3 17 37 keys", + "1 24 37 keys", "1 25 37 keys", "1 32 36 gun", + "2 23 30 skull", "2 27 33 skull", "2 30 37 skull", + "3 18 30 skull", "3 20 37 skull", "3 21 37 skull", + "1 35 37 gun", + "1", + "gun", "gun", "gun", "gun", "gun"] + assert_nothing_raised {Game.read_game(gamelines22)} + game, moves = Game.read_game(gamelines22) + assert_equal 139, game.history.length + assert_equal 0, game.current_player + assert_equal 1, game.moves_by_current_player + assert_equal 5, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 90, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 12, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :skull}).length + + assert_raise(GameWonNotice) {game.apply_move!("1 36 37 gun".to_move(game, true))} + assert_raise(InvalidMoveError) {game.apply_move!("1 36 37".to_move(game, true))} + + gamelines23 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 12 bottle", "3 6 21 bottle", "3 6 28 bottle", + "1 4 32 bottle", "1 6 37 bottle", "1 10 37 bottle", + "2 13 37 bottle", "2 14 23 keys", "2 15 27 keys", + "3 12 20 skull", "3 16 31 keys", "3 17 37 keys", + "1 24 37 keys", "1 25 37 keys", "1 32 36 gun", + "2 23 30 skull", "2 27 33 skull", "2 30 37 skull", + "3 18 30 skull", "3 20 37 skull", "3 21 37 skull", + "1 35 37 gun", "1 36 37 gun", + "1", + "gun", "gun", "gun", "gun"] + assert_raise(GameWonNotice) {Game.read_game(gamelines23)} + + gamelines24 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 12 bottle", "3 6 21 bottle", "3 6 28 bottle", + "1 4 32 bottle", "1 6 37 bottle", "1 10 37 bottle", + "2 13 37 bottle", "2 14 23 keys", "2 15 27 keys", + "3 12 20 skull", "3 16 31 keys", "3 17 37 keys", + "1 24 37 keys", "1 25 37 keys", "1 32 36 gun", + "2 23 30 skull", "2 27 33 skull", "2 30 37 skull", + "3 18 30 skull", "3 20 37 skull", "3 21 37 skull", + "1 35 37 gun", "1 36 37 gun", + "2 33 34 dagger", + "2", + "dagger", "dagger"] + assert_raise(GameWonNotice) {Game.read_game(gamelines24)} + end + +end \ No newline at end of file diff --git a/test/libcartagena_test.rb b/test/libcartagena_test.rb new file mode 100644 index 0000000..7825154 --- /dev/null +++ b/test/libcartagena_test.rb @@ -0,0 +1,2920 @@ +# == Synopsis +# +# Unit tests for the libcartagena library +# +# == Author +# Neil Smith +# +# 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 . +# +# == Change history +# Version 1.0:: 5 Dec 2008 +# => * Initial build + + +$:.unshift File.join(File.dirname(__FILE__),'..','lib', 'libcartagena') + +require 'test/unit' +require File.join(File.dirname(__FILE__), '..', 'lib', 'libcartagena') + +class TileTest < Test::Unit::TestCase + def test_tile_init + front = [:hat, :keys, :gun, :bottle, :skull, :dagger] + back = [:gun, :hat, :dagger, :skull, :bottle, :keys] + assert_nothing_raised { Tile.new(front, back) } + tile = Tile.new(front, back) + + assert_equal 6, tile.front.length + assert_equal 6, tile.back.length + assert_equal 6, tile.exposed.length + assert_equal front, tile.front.map {|p| p.symbol} + assert_equal back, tile.back.map {|p| p.symbol} + assert_equal front, tile.exposed.map {|p| p.symbol} + + tile.flip! + assert_equal 6, tile.front.length + assert_equal 6, tile.back.length + assert_equal 6, tile.exposed.length + assert_equal front, tile.front.map {|p| p.symbol} + assert_equal back, tile.back.map {|p| p.symbol} + assert_equal back, tile.exposed.map {|p| p.symbol} + + tile.flip! + assert_equal 6, tile.front.length + assert_equal 6, tile.back.length + assert_equal 6, tile.exposed.length + assert_equal front, tile.front.map {|p| p.symbol} + assert_equal back, tile.back.map {|p| p.symbol} + assert_equal front, tile.exposed.map {|p| p.symbol} + + tile.flip! + tile.reverse! + assert_equal 6, tile.front.length + assert_equal 6, tile.back.length + assert_equal 6, tile.exposed.length + assert_equal front, tile.front.map {|p| p.symbol} + assert_equal back, tile.back.map {|p| p.symbol} + assert_equal back.reverse, tile.exposed.map {|p| p.symbol} + + tile.flip! + assert_equal 6, tile.front.length + assert_equal 6, tile.back.length + assert_equal 6, tile.exposed.length + assert_equal front, tile.front.map {|p| p.symbol} + assert_equal back, tile.back.map {|p| p.symbol} + assert_equal front, tile.exposed.map {|p| p.symbol} + + tile.reverse! + assert_equal 6, tile.front.length + assert_equal 6, tile.back.length + assert_equal 6, tile.exposed.length + assert_equal front, tile.front.map {|p| p.symbol} + assert_equal back, tile.back.map {|p| p.symbol} + assert_equal front.reverse, tile.exposed.map {|p| p.symbol} + + end +end + +class BoardTest < Test::Unit::TestCase + def test_board_init + assert_nothing_raised { Board.new(6) } + board = Board.new(1) + assert_equal 1, board.tiles.length + assert_equal 8, board.positions.length + + board = Board.new(6) + assert_equal 6, board.tiles.length + assert_equal 38, board.positions.length + + 0.upto(5) do |i| + start = i * 6 + 1 + slice = board.positions[start..(start+5)] + $SYMBOLS.each do |tile| + assert_equal 1, (slice.find_all {|p| p.symbol == tile}).length + end + end + end +end + + +class GameTest < Test::Unit::TestCase + def test_game_init + assert_nothing_raised { Game.new } + + game = Game.new(6) + assert_equal 6, game.players.length + 0.upto(5) do |i| + assert_equal $INITIAL_CARDS_PER_PLAYER, game.players_cards[i].length + end + assert_equal $SYMBOLS.length * $CARDS_PER_SYMBOL - 6 * $INITIAL_CARDS_PER_PLAYER, game.deck.length + end + + def test_game_setting + game = Game.new(5,6,6) + game.set_testing_game! + game.pieces.each do |player| + player.each do |piece| + assert_equal game.board.positions[0], piece.position + end + end + assert_equal [:cell, :gun, :keys, :dagger, :hat, :skull, :bottle, + :keys, :dagger, :skull, :hat, :gun, :bottle, + :dagger, :bottle, :keys, :gun, :hat, :skull, + :dagger, :skull, :bottle, :gun, :keys, :hat, + :hat, :dagger, :keys, :bottle, :gun, :skull, + :keys, :bottle, :skull, :dagger, :hat, :gun, :boat], + game.board.positions.collect {|p| p.symbol} + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} +# assert_equal [:bottle, :keys, :skull], game.players_cards[5].sort_by {|c| c.to_s} + assert_equal 5, game.players_cards.length + assert_equal 5, game.players.length + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle, :skull], + game.deck + assert_equal 0, game.moves_by_current_player + assert_equal 18, game.possible_moves.length + game.apply_move!(game.possible_moves[0]) + assert_equal [:hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.board.positions.index(game.pieces[0][0].position) + assert_equal 87, game.deck.length + + assert_equal 1, game.moves_by_current_player + assert_equal 12, game.possible_moves.length + game.apply_move!(game.possible_moves[3]) + assert_equal [:hat], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.board.positions.index(game.pieces[0][0].position) + assert_equal 5, game.board.positions.index(game.pieces[0][1].position) + assert_equal 87, game.deck.length + + assert_equal 2, game.moves_by_current_player + assert_equal 7, game.possible_moves.length + game.apply_move!(game.possible_moves[2]) + assert_equal [:hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.board.positions.index(game.pieces[0][0].position) + assert_equal 1, game.board.positions.index(game.pieces[0][1].position) + assert_equal 86, game.deck.length + + game.reset_current_player 0 # Needed to prevent errors below + + assert_equal 0, game.moves_by_current_player + assert_equal 12, game.possible_moves.length + game.apply_move!(game.possible_moves[-1]) + assert_equal [:hat], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.board.positions.index(game.pieces[0][0].position) + assert_equal 1, game.board.positions.index(game.pieces[0][1].position) + assert_equal 5, game.board.positions.index(game.pieces[0][5].position) + assert_equal 86, game.deck.length + + assert_equal 1, game.moves_by_current_player + assert_equal 7, game.possible_moves.length + game.apply_move!(game.possible_moves[-1]) + assert_equal [:bottle, :hat, :keys], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.board.positions.index(game.pieces[0][0].position) + assert_equal 1, game.board.positions.index(game.pieces[0][1].position) + assert_equal 1, game.board.positions.index(game.pieces[0][5].position) + assert_equal 84, game.deck.length + end + + def test_game_termination + game = Game.new(6) + game.set_testing_game! + assert_equal 0, game.moves_by_current_player + + game.apply_move!(Move.new(game.pieces[0][0], game.board.positions[0], game.board.positions[36]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[0].contains.include?(game.pieces[0][1]) + assert game.board.positions[0].contains.include?(game.pieces[0][2]) + assert game.board.positions[0].contains.include?(game.pieces[0][3]) + assert game.board.positions[0].contains.include?(game.pieces[0][4]) + assert game.board.positions[0].contains.include?(game.pieces[0][5]) + assert_equal 18, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][1], game.board.positions[0], game.board.positions[35]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[0].contains.include?(game.pieces[0][2]) + assert game.board.positions[0].contains.include?(game.pieces[0][3]) + assert game.board.positions[0].contains.include?(game.pieces[0][4]) + assert game.board.positions[0].contains.include?(game.pieces[0][5]) + assert_equal 19, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][2], game.board.positions[0], game.board.positions[34]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[34].contains.include?(game.pieces[0][2]) + assert game.board.positions[0].contains.include?(game.pieces[0][3]) + assert game.board.positions[0].contains.include?(game.pieces[0][4]) + assert game.board.positions[0].contains.include?(game.pieces[0][5]) + assert_equal 20, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][3], game.board.positions[0], game.board.positions[33]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[34].contains.include?(game.pieces[0][2]) + assert game.board.positions[33].contains.include?(game.pieces[0][3]) + assert game.board.positions[0].contains.include?(game.pieces[0][4]) + assert game.board.positions[0].contains.include?(game.pieces[0][5]) + assert_equal 21, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][4], game.board.positions[0], game.board.positions[32]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[34].contains.include?(game.pieces[0][2]) + assert game.board.positions[33].contains.include?(game.pieces[0][3]) + assert game.board.positions[32].contains.include?(game.pieces[0][4]) + assert game.board.positions[0].contains.include?(game.pieces[0][5]) + assert_equal 22, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][5], game.board.positions[0], game.board.positions[31]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[34].contains.include?(game.pieces[0][2]) + assert game.board.positions[33].contains.include?(game.pieces[0][3]) + assert game.board.positions[32].contains.include?(game.pieces[0][4]) + assert game.board.positions[31].contains.include?(game.pieces[0][5]) + assert_equal 23, game.possible_moves.length + + # Now start moving pices onto the boat + game.apply_move!(Move.new(game.pieces[0][5], game.board.positions[31], game.board.positions[37]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[34].contains.include?(game.pieces[0][2]) + assert game.board.positions[33].contains.include?(game.pieces[0][3]) + assert game.board.positions[32].contains.include?(game.pieces[0][4]) + assert game.board.positions[37].contains.include?(game.pieces[0][5]) + assert_equal 20, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][4], game.board.positions[32], game.board.positions[37]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[34].contains.include?(game.pieces[0][2]) + assert game.board.positions[33].contains.include?(game.pieces[0][3]) + assert game.board.positions[37].contains.include?(game.pieces[0][4]) + assert game.board.positions[37].contains.include?(game.pieces[0][5]) + assert_equal 17, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][3], game.board.positions[33], game.board.positions[37]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[34].contains.include?(game.pieces[0][2]) + assert game.board.positions[37].contains.include?(game.pieces[0][3]) + assert game.board.positions[37].contains.include?(game.pieces[0][4]) + assert game.board.positions[37].contains.include?(game.pieces[0][5]) + assert_equal 14, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][2], game.board.positions[34], game.board.positions[37]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[35].contains.include?(game.pieces[0][1]) + assert game.board.positions[37].contains.include?(game.pieces[0][2]) + assert game.board.positions[37].contains.include?(game.pieces[0][3]) + assert game.board.positions[37].contains.include?(game.pieces[0][4]) + assert game.board.positions[37].contains.include?(game.pieces[0][5]) + assert_equal 11, game.possible_moves.length + + game.apply_move!(Move.new(game.pieces[0][1], game.board.positions[35], game.board.positions[37]), 0, false) + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert game.board.positions[36].contains.include?(game.pieces[0][0]) + assert game.board.positions[37].contains.include?(game.pieces[0][1]) + assert game.board.positions[37].contains.include?(game.pieces[0][2]) + assert game.board.positions[37].contains.include?(game.pieces[0][3]) + assert game.board.positions[37].contains.include?(game.pieces[0][4]) + assert game.board.positions[37].contains.include?(game.pieces[0][5]) + assert_equal 8, game.possible_moves.length + + assert_raise(GameWonNotice) {game.apply_move!(Move.new(game.pieces[0][0], game.board.positions[36], game.board.positions[37]), 0, false)} + + end + + def test_retreats + game = Game.new(5,6,6) + game.set_testing_game! + + game.pieces.each do |player| + player.each do |piece| + assert_equal game.board.positions[0], piece.position + end + end + assert_equal [:cell, :gun, :keys, :dagger, :hat, :skull, :bottle, + :keys, :dagger, :skull, :hat, :gun, :bottle, + :dagger, :bottle, :keys, :gun, :hat, :skull, + :dagger, :skull, :bottle, :gun, :keys, :hat, + :hat, :dagger, :keys, :bottle, :gun, :skull, + :keys, :bottle, :skull, :dagger, :hat, :gun, :boat], + game.board.positions.collect {|p| p.symbol} + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} +# assert_equal [:bottle, :keys, :skull], game.players_cards[5].sort_by {|c| c.to_s} + assert_equal 5, game.players_cards.length + assert_equal 5, game.players.length + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle, :skull], + game.deck + assert_equal 0, game.moves_by_current_player + assert_equal 18, game.possible_moves.length + game.apply_move!(game.possible_moves[0]) + assert_equal [:hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.board.positions.index(game.pieces[0][0].position) + assert_equal 87, game.deck.length + + assert_equal 1, game.moves_by_current_player + assert_equal 12, game.possible_moves.length + game.apply_move!(game.possible_moves[3]) + assert_equal [:hat], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.board.positions.index(game.pieces[0][0].position) + assert_equal 5, game.board.positions.index(game.pieces[0][1].position) + assert_equal 87, game.deck.length + + # Test can't retreat into the cell + assert_equal 2, game.moves_by_current_player + assert_equal 7, game.possible_moves.length + assert_raise(InvalidMoveError) do + game.apply_move!(Move.new(game.pieces[0][1], game.board.positions[5], game.board.positions[0])) + end + end + + # Test invalid move error trapping + def test_invalid_move_error_trapping + game = Game.new(5,6,6) + game.set_testing_game! + + game.pieces.each do |player| + player.each do |piece| + assert_equal game.board.positions[0], piece.position + end + end + assert_equal [:cell, :gun, :keys, :dagger, :hat, :skull, :bottle, + :keys, :dagger, :skull, :hat, :gun, :bottle, + :dagger, :bottle, :keys, :gun, :hat, :skull, + :dagger, :skull, :bottle, :gun, :keys, :hat, + :hat, :dagger, :keys, :bottle, :gun, :skull, + :keys, :bottle, :skull, :dagger, :hat, :gun, :boat], + game.board.positions.collect {|p| p.symbol} + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal 5, game.players_cards.length + assert_equal 5, game.players.length + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle, :skull], + game.deck + assert_equal 0, game.moves_by_current_player + + # Test if can move to a space without the right card + assert_raise(InvalidMoveError) {game.apply_move!(Move.new(game.pieces[0][0], game.board.positions[2], game.board.positions[0]))} + + + # Apply a series of moves to get an interesting game state + game.apply_move!('0 0 5'.to_move(game)) + assert_equal [:gun, :hat], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 0 4'.to_move(game)) + assert_equal [:gun], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 5 4'.to_move(game)) + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 1, game.current_player + game.apply_move!('1 0 2'.to_move(game)) + assert_equal [:bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 0 7'.to_move(game)) + assert_equal [:bottle], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 7 4'.to_move(game)) + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 2, game.current_player + game.apply_move!('2 0 7'.to_move(game)) + assert_equal [:bottle, :hat], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 0 10'.to_move(game)) + assert_equal [:bottle], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 0 6'.to_move(game)) + assert_equal [], game.players_cards[2].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 3, game.current_player + game.apply_move!('3 0 12'.to_move(game)) + assert_equal [:skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 12 10'.to_move(game)) + assert_equal [:skull, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 0 5'.to_move(game)) + assert_equal [:skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 4, game.current_player + game.apply_move!('4 0 12'.to_move(game)) + assert_equal [:gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 12 10'.to_move(game)) + assert_equal [:dagger, :gun, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 0 3'.to_move(game)) + assert_equal [:gun, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:gun, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 0, game.current_player + + game.apply_move!('0 0 9'.to_move(game)) + assert_equal [:gun], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 0 1'.to_move(game)) + assert_equal [], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 9 7'.to_move(game)) + assert_equal [:keys], game.players_cards[0].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 1, game.current_player + game.apply_move!('1 0 12'.to_move(game)) + assert_equal [:bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 0 14'.to_move(game)) + assert_equal [:keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 12 7'.to_move(game)) + assert_equal [:hat, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 2, game.current_player + game.apply_move!('2 6 5'.to_move(game)) + assert_equal [:gun], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 0 11'.to_move(game)) + assert_equal [], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 11 5'.to_move(game)) + assert_equal [:bottle, :keys], game.players_cards[2].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 3, game.current_player + game.apply_move!('3 0 9'.to_move(game)) + assert_equal [:skull], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 0 18'.to_move(game)) + assert_equal [], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 18 14'.to_move(game)) + assert_equal [:bottle], game.players_cards[3].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 4, game.current_player + game.apply_move!('4 0 11'.to_move(game)) + assert_equal [:gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 0 16'.to_move(game)) + assert_equal [:gun], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 16 14'.to_move(game)) + assert_equal [:gun, :keys, :skull], game.players_cards[4].sort_by {|c| c.to_s} + + assert_equal [:keys], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:hat, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:gun, :keys, :skull], game.players_cards[4].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 0, game.current_player + + game.apply_move!('0 0 15'.to_move(game)) + assert_equal [], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 15 11'.to_move(game)) + assert_equal [:bottle], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 11 9'.to_move(game)) + assert_equal [:bottle, :skull], game.players_cards[0].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 1, game.current_player + game.apply_move!('1 0 15'.to_move(game)) + assert_equal [:hat, :keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 0 17'.to_move(game)) + assert_equal [:keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 17 15'.to_move(game)) + assert_equal [:keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 2, game.current_player + game.apply_move!('2 0 23'.to_move(game)) + assert_equal [:bottle], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 23 15'.to_move(game)) + assert_equal [:bottle, :dagger, :hat], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 0 17'.to_move(game)) + assert_equal [:bottle, :dagger], game.players_cards[2].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 3, game.current_player + game.apply_move!('3 10 9'.to_move(game)) + assert_equal [:bottle, :dagger, :hat], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 0 24'.to_move(game)) + assert_equal [:bottle, :dagger], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 0 8'.to_move(game)) + assert_equal [:bottle], game.players_cards[3].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 4, game.current_player + game.apply_move!('4 0 16'.to_move(game)) + assert_equal [:keys, :skull], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 16 11'.to_move(game)) + assert_equal [:keys, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 11 10'.to_move(game)) + assert_equal [:dagger, :dagger, :keys, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + + assert_equal 2, game.board.positions[ 0].contains.length + assert_equal 1, game.board.positions[ 1].contains.length + assert_equal 1, game.board.positions[ 2].contains.length + assert_equal 1, game.board.positions[ 3].contains.length + assert_equal 3, game.board.positions[ 4].contains.length + assert_equal 3, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 3, game.board.positions[ 7].contains.length + assert_equal 1, game.board.positions[ 8].contains.length + assert_equal 3, game.board.positions[ 9].contains.length + assert_equal 3, game.board.positions[10].contains.length + assert_equal 1, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 3, game.board.positions[14].contains.length + assert_equal 3, game.board.positions[15].contains.length + assert_equal 0, game.board.positions[16].contains.length + assert_equal 1, game.board.positions[17].contains.length + assert_equal 0, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 0, game.board.positions[20].contains.length + assert_equal 0, game.board.positions[21].contains.length + assert_equal 0, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 1, game.board.positions[24].contains.length + assert_equal 0, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 0, game.board.positions[27].contains.length + assert_equal 0, game.board.positions[28].contains.length + assert_equal 0, game.board.positions[29].contains.length + assert_equal 0, game.board.positions[30].contains.length + assert_equal 0, game.board.positions[31].contains.length + assert_equal 0, game.board.positions[32].contains.length + assert_equal 0, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 0, game.board.positions[35].contains.length + assert_equal 0, game.board.positions[36].contains.length + assert_equal 0, game.board.positions[37].contains.length + + assert_raise(InvalidMoveError) {game.apply_move!('0 7 6'.to_move(game))} # can't retreat into an empty space + assert_raise(InvalidMoveError) {game.apply_move!('0 7 5'.to_move(game))} # can't retreat into a space with three men + assert_raise(InvalidMoveError) {game.apply_move!('0 1 0'.to_move(game))} # can't retreat into the boat + assert_raise(InvalidMoveError) {game.apply_move!('0 1 12'.to_move(game))} # can't skip a vacant space of this type + assert_raise(InvalidMoveError) {game.apply_move!('0 1 5'.to_move(game))} # can't advance to a space with three pirates + assert_raise(InvalidMoveError) {game.apply_move!('0 2 6'.to_move(game))} # can't move another's piece + assert_raise(InvalidMoveError) {game.apply_move!('0 9 9'.to_move(game))} # can't advance the same square + assert_raise(InvalidMoveError) {game.apply_move!('0 9 20'.to_move(game))} # can't skip a vacant space of this type + + game.next_player! + assert_equal 0, game.current_player + + game.apply_move!('0 0 18'.to_move(game)) + assert_equal [:bottle], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 7 12'.to_move(game)) + assert_equal [], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 12 11'.to_move(game)) + assert_equal [:dagger], game.players_cards[0].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 1, game.current_player + game.apply_move!('1 14 11'.to_move(game)) + assert_equal [:dagger, :keys, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 2 23'.to_move(game)) + assert_equal [:dagger, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 15 14'.to_move(game)) + assert_equal [:bottle, :bottle, :dagger, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 2, game.current_player + game.apply_move!('2 17 15'.to_move(game)) + assert_equal [:bottle, :dagger, :dagger, :keys], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 5 27'.to_move(game)) + assert_equal [:bottle, :dagger, :dagger], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 5 13'.to_move(game)) + assert_equal [:bottle, :dagger], game.players_cards[2].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 3, game.current_player + game.apply_move!('3 8 7'.to_move(game)) + assert_equal [:bottle, :bottle, :hat], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 5 17'.to_move(game)) + assert_equal [:bottle, :bottle], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 7 12'.to_move(game)) + assert_equal [:bottle], game.players_cards[3].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 4, game.current_player + game.apply_move!('4 0 8'.to_move(game)) + assert_equal [:dagger, :keys, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 8 7'.to_move(game)) + assert_equal [:bottle, :dagger, :keys, :keys, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 3 31'.to_move(game)) + assert_equal [:bottle, :dagger, :keys, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + + + game.next_player! + assert_equal 0, game.current_player + + game.apply_move!('0 18 17'.to_move(game)) + assert_equal [:dagger, :gun], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 1 16'.to_move(game)) + assert_equal [:dagger], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 17 16'.to_move(game)) + assert_equal [:dagger, :skull], game.players_cards[0].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 1, game.current_player + game.apply_move!('1 4 37 keys'.to_move(game)) + assert_equal [:bottle, :bottle, :dagger, :keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 7 4'.to_move(game)) + assert_equal [:bottle, :bottle, :bottle, :dagger, :hat, :keys], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 4 37 keys'.to_move(game)) + assert_equal [:bottle, :bottle, :bottle, :dagger, :hat], game.players_cards[1].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 2, game.current_player + game.apply_move!('2 7 21'.to_move(game)) + assert_equal [:dagger], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 13 12'.to_move(game)) + assert_equal [:dagger, :dagger], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 10 13'.to_move(game)) + assert_equal [:dagger], game.players_cards[2].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 3, game.current_player + game.apply_move!('3 17 16'.to_move(game)) + assert_equal [:bottle, :hat, :skull], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 9 28'.to_move(game)) + assert_equal [:hat, :skull], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 9 18'.to_move(game)) + assert_equal [:hat], game.players_cards[3].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 4, game.current_player + game.apply_move!('4 7 37 keys'.to_move(game)) + assert_equal [:bottle, :dagger, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 11 10'.to_move(game)) + assert_equal [:bottle, :dagger, :dagger, :gun, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 10 32'.to_move(game)) + assert_equal [:dagger, :dagger, :gun, :skull, :skull], game.players_cards[4].sort_by {|c| c.to_s} + + + game.next_player! + assert_equal 0, game.current_player + + game.apply_move!('0 11 10'.to_move(game)) + assert_equal [:bottle, :dagger, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 4 8'.to_move(game)) + assert_equal [:bottle, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 4 17'.to_move(game)) + assert_equal [:bottle, :skull], game.players_cards[0].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 1, game.current_player + game.apply_move!('1 11 37 bottle'.to_move(game)) + assert_equal [:bottle, :bottle, :dagger, :hat], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 14 37 bottle'.to_move(game)) + assert_equal [:bottle, :dagger, :hat], game.players_cards[1].sort_by {|c| c.to_s} + game.apply_move!('1 15 37 bottle'.to_move(game)) + assert_equal [:dagger, :hat], game.players_cards[1].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 2, game.current_player + game.apply_move!('2 13 12'.to_move(game)) + assert_equal [:dagger, :gun, :hat], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 12 22 gun'.to_move(game)) + assert_equal [:dagger, :hat], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 12 25 hat'.to_move(game)) + assert_equal [:dagger], game.players_cards[2].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 3, game.current_player + game.apply_move!('3 16 15'.to_move(game)) + assert_equal [:dagger, :gun, :hat], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 12 35 hat'.to_move(game)) + assert_equal [:dagger, :gun], game.players_cards[3].sort_by {|c| c.to_s} + game.apply_move!('3 14 29 gun'.to_move(game)) + assert_equal [:dagger], game.players_cards[3].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 4, game.current_player + game.apply_move!('4 10 20 skull'.to_move(game)) + assert_equal [:dagger, :dagger, :gun, :skull], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 10 30 skull'.to_move(game)) + assert_equal [:dagger, :dagger, :gun], game.players_cards[4].sort_by {|c| c.to_s} + game.apply_move!('4 14 36 gun'.to_move(game)) + assert_equal [:dagger, :dagger], game.players_cards[4].sort_by {|c| c.to_s} + + + game.next_player! + assert_equal 0, game.current_player + + game.apply_move!('0 17 16'.to_move(game)) + assert_equal [:bottle, :gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 8 33 skull'.to_move(game)) + assert_equal [:bottle, :gun, :hat], game.players_cards[0].sort_by {|c| c.to_s} + game.apply_move!('0 9 17 hat'.to_move(game)) + assert_equal [:bottle, :gun], game.players_cards[0].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 1, game.current_player + assert_raise(GameWonNotice) {game.apply_move!('1 23 37 hat'.to_move(game))} + assert_equal [:dagger], game.players_cards[1].sort_by {|c| c.to_s} + + assert_equal 0, game.board.positions[ 0].contains.length + assert_equal 0, game.board.positions[ 1].contains.length + assert_equal 0, game.board.positions[ 2].contains.length + assert_equal 0, game.board.positions[ 3].contains.length + assert_equal 0, game.board.positions[ 4].contains.length + assert_equal 0, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 0, game.board.positions[ 7].contains.length + assert_equal 0, game.board.positions[ 8].contains.length + assert_equal 0, game.board.positions[ 9].contains.length + assert_equal 1, game.board.positions[10].contains.length + assert_equal 0, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 0, game.board.positions[14].contains.length + assert_equal 3, game.board.positions[15].contains.length + assert_equal 3, game.board.positions[16].contains.length + assert_equal 1, game.board.positions[17].contains.length + assert_equal 1, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 1, game.board.positions[20].contains.length + assert_equal 1, game.board.positions[21].contains.length + assert_equal 1, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 1, game.board.positions[24].contains.length + assert_equal 1, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 1, game.board.positions[27].contains.length + assert_equal 1, game.board.positions[28].contains.length + assert_equal 1, game.board.positions[29].contains.length + assert_equal 1, game.board.positions[30].contains.length + assert_equal 1, game.board.positions[31].contains.length + assert_equal 1, game.board.positions[32].contains.length + assert_equal 1, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 1, game.board.positions[35].contains.length + assert_equal 1, game.board.positions[36].contains.length + assert_equal 7, game.board.positions[37].contains.length + + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun], + game.deck + + assert_equal [:bottle, :gun], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:dagger], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:dagger], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:dagger], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:dagger, :dagger], game.players_cards[4].sort_by {|c| c.to_s} + + end + # + # : move without a card + # move to an occupied space + # retreat to an emtpy space + # retreat to an overfull space + # retreat to the cell with one pirate already in it + + + # Test current player switching + + # Test undo moves + + # Test file reading + # (need a full game file to read) + + def test_undo + game = Game.new(5,6,6) + game.set_testing_game! + game.pieces.each do |player| + player.each do |piece| + assert_equal game.board.positions[0], piece.position + end + end + assert_equal [:cell, :gun, :keys, :dagger, :hat, :skull, :bottle, + :keys, :dagger, :skull, :hat, :gun, :bottle, + :dagger, :bottle, :keys, :gun, :hat, :skull, + :dagger, :skull, :bottle, :gun, :keys, :hat, + :hat, :dagger, :keys, :bottle, :gun, :skull, + :keys, :bottle, :skull, :dagger, :hat, :gun, :boat], + game.board.positions.collect {|p| p.symbol} + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal 5, game.players_cards.length + assert_equal 5, game.players.length + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle, :skull], + game.deck + + assert_equal 0, game.history.length + + game.apply_move!('0 0 5'.to_move(game)) + assert_equal [:gun, :hat], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.history.length + game.apply_move!('0 0 4'.to_move(game)) + assert_equal [:gun], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 2, game.history.length + game.apply_move!('0 5 4'.to_move(game)) + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 3, game.history.length + + game.next_player! + assert_equal 1, game.current_player + assert_equal 3, game.history.length + game.apply_move!('1 0 2'.to_move(game)) + assert_equal [:bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal 4, game.history.length + game.apply_move!('1 0 7'.to_move(game)) + assert_equal [:bottle], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal 5, game.history.length + game.apply_move!('1 7 4'.to_move(game)) + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal 6, game.history.length + assert_equal 1, game.current_player + assert_equal 3, game.moves_by_current_player + + assert_equal 26, game.board.positions[ 0].contains.length + assert_equal 0, game.board.positions[ 1].contains.length + assert_equal 1, game.board.positions[ 2].contains.length + assert_equal 0, game.board.positions[ 3].contains.length + assert_equal 3, game.board.positions[ 4].contains.length + assert_equal 0, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 0, game.board.positions[ 7].contains.length + assert_equal 0, game.board.positions[ 8].contains.length + assert_equal 0, game.board.positions[ 9].contains.length + assert_equal 0, game.board.positions[10].contains.length + assert_equal 0, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 0, game.board.positions[14].contains.length + assert_equal 0, game.board.positions[15].contains.length + assert_equal 0, game.board.positions[16].contains.length + assert_equal 0, game.board.positions[17].contains.length + assert_equal 0, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 0, game.board.positions[20].contains.length + assert_equal 0, game.board.positions[21].contains.length + assert_equal 0, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 0, game.board.positions[24].contains.length + assert_equal 0, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 0, game.board.positions[27].contains.length + assert_equal 0, game.board.positions[28].contains.length + assert_equal 0, game.board.positions[29].contains.length + assert_equal 0, game.board.positions[30].contains.length + assert_equal 0, game.board.positions[31].contains.length + assert_equal 0, game.board.positions[32].contains.length + assert_equal 0, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 0, game.board.positions[35].contains.length + assert_equal 0, game.board.positions[36].contains.length + assert_equal 0, game.board.positions[37].contains.length + + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull], + game.deck + + game.undo_move! # player 1's third move + + assert_equal 5, game.history.length + + assert_equal 26, game.board.positions[ 0].contains.length + assert_equal 0, game.board.positions[ 1].contains.length + assert_equal 1, game.board.positions[ 2].contains.length + assert_equal 0, game.board.positions[ 3].contains.length + assert_equal 2, game.board.positions[ 4].contains.length + assert_equal 0, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 1, game.board.positions[ 7].contains.length + assert_equal 0, game.board.positions[ 8].contains.length + assert_equal 0, game.board.positions[ 9].contains.length + assert_equal 0, game.board.positions[10].contains.length + assert_equal 0, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 0, game.board.positions[14].contains.length + assert_equal 0, game.board.positions[15].contains.length + assert_equal 0, game.board.positions[16].contains.length + assert_equal 0, game.board.positions[17].contains.length + assert_equal 0, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 0, game.board.positions[20].contains.length + assert_equal 0, game.board.positions[21].contains.length + assert_equal 0, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 0, game.board.positions[24].contains.length + assert_equal 0, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 0, game.board.positions[27].contains.length + assert_equal 0, game.board.positions[28].contains.length + assert_equal 0, game.board.positions[29].contains.length + assert_equal 0, game.board.positions[30].contains.length + assert_equal 0, game.board.positions[31].contains.length + assert_equal 0, game.board.positions[32].contains.length + assert_equal 0, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 0, game.board.positions[35].contains.length + assert_equal 0, game.board.positions[36].contains.length + assert_equal 0, game.board.positions[37].contains.length + + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle], + game.deck + assert_equal 1, game.current_player + assert_equal 2, game.moves_by_current_player + + # Re-apply player 1's third move + game.apply_move!('1 7 4'.to_move(game)) + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + + game.next_player! + assert_equal 2, game.current_player + game.apply_move!('2 0 7'.to_move(game)) + assert_equal [:bottle, :hat], game.players_cards[2].sort_by {|c| c.to_s} + game.apply_move!('2 0 10'.to_move(game)) + assert_equal [:bottle], game.players_cards[2].sort_by {|c| c.to_s} + + assert_equal 8, game.history.length + + assert_equal 24, game.board.positions[ 0].contains.length + assert_equal 0, game.board.positions[ 1].contains.length + assert_equal 1, game.board.positions[ 2].contains.length + assert_equal 0, game.board.positions[ 3].contains.length + assert_equal 3, game.board.positions[ 4].contains.length + assert_equal 0, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 1, game.board.positions[ 7].contains.length + assert_equal 0, game.board.positions[ 8].contains.length + assert_equal 0, game.board.positions[ 9].contains.length + assert_equal 1, game.board.positions[10].contains.length + assert_equal 0, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 0, game.board.positions[14].contains.length + assert_equal 0, game.board.positions[15].contains.length + assert_equal 0, game.board.positions[16].contains.length + assert_equal 0, game.board.positions[17].contains.length + assert_equal 0, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 0, game.board.positions[20].contains.length + assert_equal 0, game.board.positions[21].contains.length + assert_equal 0, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 0, game.board.positions[24].contains.length + assert_equal 0, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 0, game.board.positions[27].contains.length + assert_equal 0, game.board.positions[28].contains.length + assert_equal 0, game.board.positions[29].contains.length + assert_equal 0, game.board.positions[30].contains.length + assert_equal 0, game.board.positions[31].contains.length + assert_equal 0, game.board.positions[32].contains.length + assert_equal 0, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 0, game.board.positions[35].contains.length + assert_equal 0, game.board.positions[36].contains.length + assert_equal 0, game.board.positions[37].contains.length + + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull], + game.deck + assert_equal 2, game.current_player + assert_equal 2, game.moves_by_current_player + + game.undo_move! # player 2's second move + + assert_equal 7, game.history.length + + assert_equal 25, game.board.positions[ 0].contains.length + assert_equal 0, game.board.positions[ 1].contains.length + assert_equal 1, game.board.positions[ 2].contains.length + assert_equal 0, game.board.positions[ 3].contains.length + assert_equal 3, game.board.positions[ 4].contains.length + assert_equal 0, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 1, game.board.positions[ 7].contains.length + assert_equal 0, game.board.positions[ 8].contains.length + assert_equal 0, game.board.positions[ 9].contains.length + assert_equal 0, game.board.positions[10].contains.length + assert_equal 0, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 0, game.board.positions[14].contains.length + assert_equal 0, game.board.positions[15].contains.length + assert_equal 0, game.board.positions[16].contains.length + assert_equal 0, game.board.positions[17].contains.length + assert_equal 0, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 0, game.board.positions[20].contains.length + assert_equal 0, game.board.positions[21].contains.length + assert_equal 0, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 0, game.board.positions[24].contains.length + assert_equal 0, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 0, game.board.positions[27].contains.length + assert_equal 0, game.board.positions[28].contains.length + assert_equal 0, game.board.positions[29].contains.length + assert_equal 0, game.board.positions[30].contains.length + assert_equal 0, game.board.positions[31].contains.length + assert_equal 0, game.board.positions[32].contains.length + assert_equal 0, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 0, game.board.positions[35].contains.length + assert_equal 0, game.board.positions[36].contains.length + assert_equal 0, game.board.positions[37].contains.length + + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull], + game.deck + assert_equal 2, game.current_player + assert_equal 1, game.moves_by_current_player + + + game.undo_move! # Player 2's first move + + assert_equal 6, game.history.length + + assert_equal 26, game.board.positions[ 0].contains.length + assert_equal 0, game.board.positions[ 1].contains.length + assert_equal 1, game.board.positions[ 2].contains.length + assert_equal 0, game.board.positions[ 3].contains.length + assert_equal 3, game.board.positions[ 4].contains.length + assert_equal 0, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 0, game.board.positions[ 7].contains.length + assert_equal 0, game.board.positions[ 8].contains.length + assert_equal 0, game.board.positions[ 9].contains.length + assert_equal 0, game.board.positions[10].contains.length + assert_equal 0, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 0, game.board.positions[14].contains.length + assert_equal 0, game.board.positions[15].contains.length + assert_equal 0, game.board.positions[16].contains.length + assert_equal 0, game.board.positions[17].contains.length + assert_equal 0, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 0, game.board.positions[20].contains.length + assert_equal 0, game.board.positions[21].contains.length + assert_equal 0, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 0, game.board.positions[24].contains.length + assert_equal 0, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 0, game.board.positions[27].contains.length + assert_equal 0, game.board.positions[28].contains.length + assert_equal 0, game.board.positions[29].contains.length + assert_equal 0, game.board.positions[30].contains.length + assert_equal 0, game.board.positions[31].contains.length + assert_equal 0, game.board.positions[32].contains.length + assert_equal 0, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 0, game.board.positions[35].contains.length + assert_equal 0, game.board.positions[36].contains.length + assert_equal 0, game.board.positions[37].contains.length + + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :bottle, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull], + game.deck + assert_equal 2, game.current_player + assert_equal 0, game.moves_by_current_player + + + game.undo_move! # Player 1's third move + + assert_equal 5, game.history.length + + assert_equal 26, game.board.positions[ 0].contains.length + assert_equal 0, game.board.positions[ 1].contains.length + assert_equal 1, game.board.positions[ 2].contains.length + assert_equal 0, game.board.positions[ 3].contains.length + assert_equal 2, game.board.positions[ 4].contains.length + assert_equal 0, game.board.positions[ 5].contains.length + assert_equal 0, game.board.positions[ 6].contains.length + assert_equal 1, game.board.positions[ 7].contains.length + assert_equal 0, game.board.positions[ 8].contains.length + assert_equal 0, game.board.positions[ 9].contains.length + assert_equal 0, game.board.positions[10].contains.length + assert_equal 0, game.board.positions[11].contains.length + assert_equal 0, game.board.positions[12].contains.length + assert_equal 0, game.board.positions[13].contains.length + assert_equal 0, game.board.positions[14].contains.length + assert_equal 0, game.board.positions[15].contains.length + assert_equal 0, game.board.positions[16].contains.length + assert_equal 0, game.board.positions[17].contains.length + assert_equal 0, game.board.positions[18].contains.length + assert_equal 0, game.board.positions[19].contains.length + assert_equal 0, game.board.positions[20].contains.length + assert_equal 0, game.board.positions[21].contains.length + assert_equal 0, game.board.positions[22].contains.length + assert_equal 0, game.board.positions[23].contains.length + assert_equal 0, game.board.positions[24].contains.length + assert_equal 0, game.board.positions[25].contains.length + assert_equal 0, game.board.positions[26].contains.length + assert_equal 0, game.board.positions[27].contains.length + assert_equal 0, game.board.positions[28].contains.length + assert_equal 0, game.board.positions[29].contains.length + assert_equal 0, game.board.positions[30].contains.length + assert_equal 0, game.board.positions[31].contains.length + assert_equal 0, game.board.positions[32].contains.length + assert_equal 0, game.board.positions[33].contains.length + assert_equal 0, game.board.positions[34].contains.length + assert_equal 0, game.board.positions[35].contains.length + assert_equal 0, game.board.positions[36].contains.length + assert_equal 0, game.board.positions[37].contains.length + + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle], + game.deck + assert_equal 1, game.current_player + assert_equal 2, game.moves_by_current_player + + end + + def test_apply_moves + game = Game.new(5,6,6) + game.set_testing_game! + game.pieces.each do |player| + player.each do |piece| + assert_equal game.board.positions[0], piece.position + end + end + assert_equal [:cell, :gun, :keys, :dagger, :hat, :skull, :bottle, + :keys, :dagger, :skull, :hat, :gun, :bottle, + :dagger, :bottle, :keys, :gun, :hat, :skull, + :dagger, :skull, :bottle, :gun, :keys, :hat, + :hat, :dagger, :keys, :bottle, :gun, :skull, + :keys, :bottle, :skull, :dagger, :hat, :gun, :boat], + game.board.positions.collect {|p| p.symbol} + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal 5, game.players_cards.length + assert_equal 5, game.players.length + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle, :skull], + game.deck + + assert_equal 0, game.history.length + + game.apply_move!('0 0 5'.to_move(game)) + assert_equal [:gun, :hat], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 1, game.history.length + game.apply_move!('0 0 4'.to_move(game)) + assert_equal [:gun], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 2, game.history.length + game.apply_move!('0 5 4'.to_move(game)) + assert_equal [:gun, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal 3, game.history.length + + game.next_player! + assert_equal 1, game.current_player + assert_equal 3, game.history.length + game.apply_move!('1 0 2'.to_move(game)) + end + + def test_read_game_lines + gamelines1 = ["5", "cell", "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 5 skull", "1 0 4 hat", + "1", + "dagger"] + assert_nothing_raised {Game.read_game(gamelines1)} + game, moves = Game.read_game(gamelines1) + assert_equal 2, game.history.length + assert_equal 0, game.current_player + assert_equal 2, game.moves_by_current_player + assert_equal 1, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 15, game.deck.length + + game.apply_move!('0 5 4'.to_move(game)) + assert_equal 3, game.history.length + assert_equal 0, game.current_player + assert_equal 3, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 16, game.deck.length + + gamelines2 = ["5", "cell", "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 5", "1 0 4", "1 5 4", + "2", + "dagger", "hat", "keys"] + assert_nothing_raised {Game.read_game(gamelines2)} + game, moves = Game.read_game(gamelines2) + assert_equal 3, game.history.length + assert_equal 1, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 16, game.deck.length + + game.apply_move!('1 0 2'.to_move(game)) + assert_equal 4, game.history.length + assert_equal 1, game.current_player + assert_equal 1, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 2, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 16, game.deck.length + + gamelines3 = ["5", "cell", "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 5", "1 0 4", "1 5 4", "2 0 2", + "2", + "dagger", "keys"] + assert_nothing_raised {Game.read_game(gamelines3)} + game, moves = Game.read_game(gamelines3) + assert_equal 4, game.history.length + assert_equal 1, game.current_player + assert_equal 1, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 2, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 16, game.deck.length + + game.apply_move!('1 0 7'.to_move(game)) + assert_equal 5, game.history.length + assert_equal 1, game.current_player + assert_equal 2, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 1, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 16, game.deck.length + + gamelines4 = ["5", "cell", "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 5", "1 0 4", "1 5 4", "2 0 2", "2 0 7", + "2", + "dagger"] + assert_nothing_raised {Game.read_game(gamelines4)} + game, moves = Game.read_game(gamelines4) + assert_equal 5, game.history.length + assert_equal 1, game.current_player + assert_equal 2, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 1, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 16, game.deck.length + + game.apply_move!('1 7 4'.to_move(game)) + assert_equal 6, game.history.length + assert_equal 1, game.current_player + assert_equal 3, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 18, game.deck.length + + gamelines5 = ["5", "cell", "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 5", "1 0 4", "1 5 4", "2 0 2", "2 0 7", "2 7 4", + "2", + "bottle", "hat", "keys"] + assert_nothing_raised {Game.read_game(gamelines5)} + game, moves = Game.read_game(gamelines5) + assert_equal 6, game.history.length + assert_equal 1, game.current_player + assert_equal 3, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 18, game.deck.length + + game.players_cards[2] << :keys + game.players_cards[2] << :hat + game.apply_move!('3 0 7'.to_move(game, true), 2) + assert_equal 7, game.history.length + assert_equal 2, game.current_player + assert_equal 1, game.moves_by_current_player + game.apply_move!('3 0 10'.to_move(game, true)) + assert_equal 8, game.history.length + assert_equal 2, game.current_player + assert_equal 2, game.moves_by_current_player + + gamelines6 = ["5", "cell", "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 5", "1 0 4", "1 5 4", "2 0 2", "2 0 7", "2 7 4", + "3", + "bottle", "hat", "keys"] + assert_nothing_raised {Game.read_game(gamelines6)} + game, moves = Game.read_game(gamelines6) + assert_equal 6, game.history.length + assert_equal 2, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 18, game.deck.length + + game.apply_move!('2 0 7'.to_move(game)) + assert_equal 7, game.history.length + assert_equal 2, game.current_player + assert_equal 1, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 2, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 18, game.deck.length + + game.apply_move!('2 0 10'.to_move(game)) + assert_equal 8, game.history.length + assert_equal 2, game.current_player + assert_equal 2, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 1, game.players_cards[2].length + assert_equal 3, game.players_cards[3].length + assert_equal 3, game.players_cards[4].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 18, game.deck.length + + # Need to do a test with a very long sequence of moves, + # to test that the card dealing works properly when the deck is exhausted + + end + + def test_dealing + game = Game.new(5,6,6) + game.set_testing_game! + 0.upto(4) do |i| + assert_equal $INITIAL_CARDS_PER_PLAYER, game.players_cards[i].length + end + assert_equal $SYMBOLS.length * $CARDS_PER_SYMBOL - 5 * $INITIAL_CARDS_PER_PLAYER, game.deck.length + assert_equal [:gun, :hat, :skull], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:bottle, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :hat, :keys], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:bottle, :skull, :skull], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [:bottle, :gun, :gun], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal 5, game.players_cards.length + assert_equal 5, game.players.length + assert_equal [:hat, :dagger, :hat, :hat, :skull, :bottle, :bottle, :bottle, :dagger, + :keys, :hat, :dagger, :skull, :skull, :dagger, :gun, :skull, :gun, + :gun, :skull, :keys, :keys, :gun, :hat, :dagger, :keys, :dagger, + :hat, :gun, :skull, :keys, :gun, :skull, :gun, :hat, :gun, :dagger, + :gun, :gun, :hat, :hat, :bottle, :gun, :dagger, :hat, :skull, :dagger, + :bottle, :hat, :skull, :gun, :bottle, :keys, :hat, :bottle, :keys, + :dagger, :bottle, :bottle, :dagger, :keys, :dagger, :dagger, :dagger, + :skull, :hat, :dagger, :dagger, :hat, :keys, :skull, :bottle, :skull, + :keys, :bottle, :keys, :bottle, :gun, :keys, :hat, :keys, :dagger, + :gun, :skull, :keys, :bottle, :skull], + game.deck + game.deal_cards!(86, 0) + assert_equal 89, game.players_cards[0].length + assert_equal 1, game.deck.length + assert_equal [:hat], game.deck + + game.players_cards[0] = [:hat, :hat, :hat, :hat, :hat] + game.players_cards[1] = [:keys, :keys, :keys] + game.players_cards[2] = [:bottle, :dagger, :skull] + game.players_cards[3] = [] + game.players_cards[4] = [] + game.deal_cards!(1, 3) + + assert_equal [:hat, :hat, :hat, :hat, :hat], game.players_cards[0].sort_by {|c| c.to_s} + assert_equal [:keys, :keys, :keys], game.players_cards[1].sort_by {|c| c.to_s} + assert_equal [:bottle, :dagger, :skull], game.players_cards[2].sort_by {|c| c.to_s} + assert_equal [:hat], game.players_cards[3].sort_by {|c| c.to_s} + assert_equal [], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal [], game.deck + + game.deal_cards!(1, 4) + assert_equal [:keys], game.players_cards[4].sort_by {|c| c.to_s} + assert_equal $CARDS_PER_SYMBOL - 6, game.deck.select {|c| c == :hat}.length + assert_equal $CARDS_PER_SYMBOL - 4, game.deck.select {|c| c == :keys}.length + assert_equal $CARDS_PER_SYMBOL - 1, game.deck.select {|c| c == :bottle}.length + assert_equal $CARDS_PER_SYMBOL - 1, game.deck.select {|c| c == :dagger}.length + assert_equal $CARDS_PER_SYMBOL - 1, game.deck.select {|c| c == :skull}.length + assert_equal $CARDS_PER_SYMBOL, game.deck.select {|c| c == :gun}.length + assert_equal $CARDS_PER_SYMBOL * 6 - 13, game.deck.length + + end + + def test_long_deal + gamelines0 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1", + "hat", "skull", "bottle"] + assert_nothing_raised {Game.read_game(gamelines0)} + game, moves = Game.read_game(gamelines0) + assert_equal 0, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 3, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 9, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 9, game.deck.length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :skull}).length + + gamelines1 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1 gun", "1 0 2 keys", "1 2 1", + "2", + "hat", "skull", "bottle"] + assert_nothing_raised {Game.read_game(gamelines1)} + game, moves = Game.read_game(gamelines1) + assert_equal 3, game.history.length + assert_equal 1, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 2, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 10, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 2, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 10, game.deck.length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 1, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 1, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :skull}).length + + gamelines2 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1", + "hat"] + assert_nothing_raised {Game.read_game(gamelines2)} + game, moves = Game.read_game(gamelines2) + assert_equal 18, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 1, game.players_cards[0].length + assert_equal 1, game.players_cards[1].length + assert_equal 1, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 6), game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 12, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 6), game.deck.length + assert_equal $CARDS_PER_SYMBOL - 2, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 2, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 2, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 2, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 2, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 2, (unplayed_cards.select {|c| c == :skull}).length + + gamelines2 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1", + "hat", "skull", "bottle"] + assert_nothing_raised {Game.read_game(gamelines2)} + game, moves = Game.read_game(gamelines2) + assert_equal 30, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 3, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 18), game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 18, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 18), game.deck.length + assert_equal $CARDS_PER_SYMBOL - 3, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 3, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 3, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 3, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 3, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 3, (unplayed_cards.select {|c| c == :skull}).length + + gamelines3 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1", + "hat", "skull", "bottle", "gun", "dagger"] + assert_nothing_raised {Game.read_game(gamelines3)} + game, moves = Game.read_game(gamelines3) + assert_equal 42, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 5, game.players_cards[0].length + assert_equal 5, game.players_cards[1].length + assert_equal 5, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 30), game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 24, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 30), game.deck.length + assert_equal $CARDS_PER_SYMBOL - 4, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 4, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 4, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 4, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 4, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 4, (unplayed_cards.select {|c| c == :skull}).length + + gamelines4 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1", + "hat", "skull", "bottle", "gun", "dagger", "keys", "hat"] + assert_nothing_raised {Game.read_game(gamelines4)} + game, moves = Game.read_game(gamelines4) + assert_equal 54, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 7, game.players_cards[0].length + assert_equal 7, game.players_cards[1].length + assert_equal 7, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 42), game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 30, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 42), game.deck.length + assert_equal $CARDS_PER_SYMBOL - 5, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 5, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 5, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 5, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 5, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 5, (unplayed_cards.select {|c| c == :skull}).length + + + gamelines5 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1", + "hat", "skull", "bottle", "gun", "dagger", "keys", "hat", "skull", "bottle", "gun", "dagger"] + assert_nothing_raised {Game.read_game(gamelines5)} + game, moves = Game.read_game(gamelines5) + assert_equal 78, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 11, game.players_cards[0].length + assert_equal 11, game.players_cards[1].length + assert_equal 11, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 66), game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 42, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 66), game.deck.length + assert_equal $CARDS_PER_SYMBOL - 7, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 7, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 7, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 7, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 7, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 7, (unplayed_cards.select {|c| c == :skull}).length + + gamelines6 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle"] + assert_nothing_raised {Game.read_game(gamelines6)} + game, moves = Game.read_game(gamelines6) + assert_equal 102, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 15, game.players_cards[0].length + assert_equal 15, game.players_cards[1].length + assert_equal 15, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 90), game.deck.length + assert_equal 3, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 54, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - (9 + 90), game.deck.length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :skull}).length + + gamelines7 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", + "1", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle", "gun"] + assert_nothing_raised {Game.read_game(gamelines7)} + game, moves = Game.read_game(gamelines7) + assert_equal 104, game.history.length + assert_equal 0, game.current_player + assert_equal 2, game.moves_by_current_player + assert_equal 16, game.players_cards[0].length + assert_equal 15, game.players_cards[1].length + assert_equal 15, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 55, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :skull}).length + + gamelines8 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "1", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "skull", "bottle", "dagger"] + assert_nothing_raised {Game.read_game(gamelines8)} + game, moves = Game.read_game(gamelines8) + assert_equal 105, game.history.length + assert_equal 0, game.current_player + assert_equal 3, game.moves_by_current_player + assert_equal 15, game.players_cards[0].length + assert_equal 15, game.players_cards[1].length + assert_equal 15, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 56, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 9, (unplayed_cards.select {|c| c == :skull}).length + + + gamelines9 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 12", "2 12 10", + "2", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle"] + assert_nothing_raised {Game.read_game(gamelines9)} + game, moves = Game.read_game(gamelines9) + assert_equal 107, game.history.length + assert_equal 1, game.current_player + assert_equal 2, game.moves_by_current_player + assert_equal 15, game.players_cards[0].length + assert_equal 15, game.players_cards[1].length + assert_equal 15, game.players_cards[2].length + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 45, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 0, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 0, (unplayed_cards.select {|c| c == :skull}).length + + gamelines10 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "1", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "hat", "hat", "hat", "hat", "hat", + "hat", "hat", "hat"] + assert_raise(InvalidMoveError) {Game.read_game(gamelines10)} # Not enough skull cards to go round + + gamelines11 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "skull", "bottle", "dagger"] + assert_nothing_raised {Game.read_game(gamelines11)} + game, moves = Game.read_game(gamelines11) + assert_equal 111, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 15, game.players_cards[0].length + assert_equal 12, game.players_cards[1].length + assert_equal 12, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 62, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :skull}).length + + gamelines12 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2", + "hat", "skull", "bottle", "gun", "dagger", "keys", + "hat", "skull", "bottle", "gun", "dagger", "keys"] + assert_nothing_raised {Game.read_game(gamelines12)} + game, moves = Game.read_game(gamelines12) + assert_equal 114, game.history.length + assert_equal 1, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 12, game.players_cards[0].length + assert_equal 12, game.players_cards[1].length + assert_equal 12, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 65, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 14, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :skull}).length + + gamelines13 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3", + "skull", "skull", "bottle", "gun", "dagger", "keys", + "bottle", "skull", "bottle", "gun", "dagger", "keys"] + assert_nothing_raised {Game.read_game(gamelines13)} + game, moves = Game.read_game(gamelines13) + assert_equal 117, game.history.length + assert_equal 2, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 12, game.players_cards[0].length + assert_equal 9, game.players_cards[1].length + assert_equal 12, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 68, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :skull}).length + + gamelines14 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 37", + "3", + "skull", "skull", "bottle", "gun", "dagger", "keys", + "bottle", "skull", "bottle", "gun", "dagger"] + assert_raise(InvalidMoveError) {Game.read_game(gamelines14)} + + gamelines15 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 12 bottle", "3 6 21 bottle", "3 6 28 bottle", + "1", + "skull", "skull", "bottle", "gun", "dagger", "keys", + "bottle", "skull", "bottle", "gun", "dagger", "keys"] + assert_nothing_raised {Game.read_game(gamelines15)} + game, moves = Game.read_game(gamelines15) + assert_equal 120, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 12, game.players_cards[0].length + assert_equal 9, game.players_cards[1].length + assert_equal 9, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 71, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 13, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :skull}).length + + gamelines16 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 12 bottle", "3 6 21 bottle", "3 6 28 bottle", + "1 4 32 bottle", "1 6 37 bottle", "1 10 37 bottle", + "2", + "skull", "skull", "keys", "gun", "dagger", "keys", + "skull", "skull", "skull"] + assert_nothing_raised {Game.read_game(gamelines16)} + game, moves = Game.read_game(gamelines16) + assert_equal 123, game.history.length + assert_equal 1, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 9, game.players_cards[0].length + assert_equal 9, game.players_cards[1].length + assert_equal 9, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 74, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 16, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :skull}).length + + gamelines17 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 12 bottle", "3 6 21 bottle", "3 6 28 bottle", + "1 4 32 bottle", "1 6 37 bottle", "1 10 37 bottle", + "2 13 37 bottle", "2 14 23 keys", "2 15 27 keys", + "3", + "skull", "skull", "keys", "gun", "dagger", "keys", + "skull", "skull", "skull"] + assert_nothing_raised {Game.read_game(gamelines17)} + game, moves = Game.read_game(gamelines17) + assert_equal 126, game.history.length + assert_equal 2, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 9, game.players_cards[0].length + assert_equal 6, game.players_cards[1].length + assert_equal 9, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 77, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 13, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :skull}).length + + gamelines18 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 12 bottle", "3 6 21 bottle", "3 6 28 bottle", + "1 4 32 bottle", "1 6 37 bottle", "1 10 37 bottle", + "2 13 37 bottle", "2 14 23 keys", "2 15 27 keys", + "3 12 20 skull", "3 16 31 keys", "3 17 37 keys", + "1", + "skull", "skull", "keys", "gun", "dagger", "keys", + "skull", "skull", "skull"] + assert_nothing_raised {Game.read_game(gamelines18)} + game, moves = Game.read_game(gamelines18) + assert_equal 129, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 9, game.players_cards[0].length + assert_equal 6, game.players_cards[1].length + assert_equal 6, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 80, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 15, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :skull}).length + + gamelines19 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 12 bottle", "3 6 21 bottle", "3 6 28 bottle", + "1 4 32 bottle", "1 6 37 bottle", "1 10 37 bottle", + "2 13 37 bottle", "2 14 23 keys", "2 15 27 keys", + "3 12 20 skull", "3 16 31 keys", "3 17 37 keys", + "1 24 37 keys", "1 25 37 keys", "1 32 36 gun", + "2", + "skull", "skull", "gun", "gun", "dagger", "gun"] + assert_nothing_raised {Game.read_game(gamelines19)} + game, moves = Game.read_game(gamelines19) + assert_equal 132, game.history.length + assert_equal 1, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 6, game.players_cards[0].length + assert_equal 6, game.players_cards[1].length + assert_equal 6, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 83, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :skull}).length + + gamelines20 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 12 bottle", "3 6 21 bottle", "3 6 28 bottle", + "1 4 32 bottle", "1 6 37 bottle", "1 10 37 bottle", + "2 13 37 bottle", "2 14 23 keys", "2 15 27 keys", + "3 12 20 skull", "3 16 31 keys", "3 17 37 keys", + "1 24 37 keys", "1 25 37 keys", "1 32 36 gun", + "2 23 30 skull", "2 27 33 skull", "2 30 37 skull", + "3", + "skull", "skull", "gun", "gun", "dagger", "gun"] + assert_nothing_raised {Game.read_game(gamelines20)} + game, moves = Game.read_game(gamelines20) + assert_equal 135, game.history.length + assert_equal 2, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 6, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 6, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 86, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 14, (unplayed_cards.select {|c| c == :skull}).length + + gamelines21 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 12 bottle", "3 6 21 bottle", "3 6 28 bottle", + "1 4 32 bottle", "1 6 37 bottle", "1 10 37 bottle", + "2 13 37 bottle", "2 14 23 keys", "2 15 27 keys", + "3 12 20 skull", "3 16 31 keys", "3 17 37 keys", + "1 24 37 keys", "1 25 37 keys", "1 32 36 gun", + "2 23 30 skull", "2 27 33 skull", "2 30 37 skull", + "3 18 30 skull", "3 20 37 skull", "3 21 37 skull", + "1", + "gun", "gun", "gun", "gun", "gun", "gun"] + assert_nothing_raised {Game.read_game(gamelines21)} + game, moves = Game.read_game(gamelines21) + assert_equal 138, game.history.length + assert_equal 0, game.current_player + assert_equal 0, game.moves_by_current_player + assert_equal 6, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 89, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 11, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :skull}).length + + gamelines22 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 12 bottle", "3 6 21 bottle", "3 6 28 bottle", + "1 4 32 bottle", "1 6 37 bottle", "1 10 37 bottle", + "2 13 37 bottle", "2 14 23 keys", "2 15 27 keys", + "3 12 20 skull", "3 16 31 keys", "3 17 37 keys", + "1 24 37 keys", "1 25 37 keys", "1 32 36 gun", + "2 23 30 skull", "2 27 33 skull", "2 30 37 skull", + "3 18 30 skull", "3 20 37 skull", "3 21 37 skull", + "1 35 37 gun", + "1", + "gun", "gun", "gun", "gun", "gun"] + assert_nothing_raised {Game.read_game(gamelines22)} + game, moves = Game.read_game(gamelines22) + assert_equal 139, game.history.length + assert_equal 0, game.current_player + assert_equal 1, game.moves_by_current_player + assert_equal 5, game.players_cards[0].length + assert_equal 3, game.players_cards[1].length + assert_equal 3, game.players_cards[2].length + assert_equal 1, game.deck.length + unplayed_cards = game.players_cards.inject(game.deck.dup) {|acc, cs| acc.concat cs} + assert_equal $CARDS_PER_SYMBOL * $SYMBOLS.length - 90, unplayed_cards.length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :bottle}).length + assert_equal $CARDS_PER_SYMBOL - 10, (unplayed_cards.select {|c| c == :dagger}).length + assert_equal $CARDS_PER_SYMBOL - 12, (unplayed_cards.select {|c| c == :gun}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :hat}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :keys}).length + assert_equal $CARDS_PER_SYMBOL - 17, (unplayed_cards.select {|c| c == :skull}).length + + assert_raise(GameWonNotice) {game.apply_move!("1 36 37 gun".to_move(game, true))} + assert_raise(InvalidMoveError) {game.apply_move!("1 36 37".to_move(game, true))} + + gamelines23 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 12 bottle", "3 6 21 bottle", "3 6 28 bottle", + "1 4 32 bottle", "1 6 37 bottle", "1 10 37 bottle", + "2 13 37 bottle", "2 14 23 keys", "2 15 27 keys", + "3 12 20 skull", "3 16 31 keys", "3 17 37 keys", + "1 24 37 keys", "1 25 37 keys", "1 32 36 gun", + "2 23 30 skull", "2 27 33 skull", "2 30 37 skull", + "3 18 30 skull", "3 20 37 skull", "3 21 37 skull", + "1 35 37 gun", "1 36 37 gun", + "1", + "gun", "gun", "gun", "gun"] + assert_raise(GameWonNotice) {Game.read_game(gamelines23)} + + gamelines24 = ["3", "cell", + "gun", "keys", "dagger", "hat", "skull", "bottle", + "keys", "dagger", "skull", "hat", "gun", "bottle", + "dagger", "bottle", "keys", "gun", "hat", "skull", + "dagger", "skull", "bottle", "gun", "keys", "hat", + "hat", "dagger", "keys", "bottle", "gun", "skull", + "keys", "bottle", "skull", "dagger", "hat", "gun", "boat", + "1 0 1", "1 0 2", "1 2 1", + "2 0 2", "2 0 3", "2 3 2", + "3 0 3", "3 0 4", "3 4 3", + "1 0 4", "1 0 5", "1 5 4", + "2 0 5", "2 0 6", "2 6 5", + "3 0 6", "3 0 11", "3 11 6", + "1 0 7", "1 7 6", + "2 0 8", "2 8 5", + "3 0 9", "3 9 4", + "1 0 10", "1 10 3", + "2 0 11", "2 11 2", + "3 0 12", "3 12 1", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "2 2 8", "2 8 2", "3 3 9", "3 9 3", + "1 1 10", "1 10 1", "2 2 11", "2 11 2", "3 3 12", "3 12 3", + "1 1 7", "1 7 1", "1 1 10", + "2 2 13 dagger", "2 2 14 bottle", "2 2 15 keys", + "3 1 16 gun", "3 3 17 hat", "3 3 18 skull", + "1 1 24 hat", "1 3 25 hat", "1 4 35 hat", + "2 5 37 hat", "2 5 37 hat", "2 5 37 hat", + "3 4 12 bottle", "3 6 21 bottle", "3 6 28 bottle", + "1 4 32 bottle", "1 6 37 bottle", "1 10 37 bottle", + "2 13 37 bottle", "2 14 23 keys", "2 15 27 keys", + "3 12 20 skull", "3 16 31 keys", "3 17 37 keys", + "1 24 37 keys", "1 25 37 keys", "1 32 36 gun", + "2 23 30 skull", "2 27 33 skull", "2 30 37 skull", + "3 18 30 skull", "3 20 37 skull", "3 21 37 skull", + "1 35 37 gun", "1 36 37 gun", + "2 33 34 dagger", + "2", + "dagger", "dagger"] + assert_raise(GameWonNotice) {Game.read_game(gamelines24)} + end + +end \ No newline at end of file -- 2.34.1