From: Neil Smith Date: Fri, 25 Sep 2009 15:27:26 +0000 (+0100) Subject: Initial commit X-Git-Url: https://git.njae.me.uk/?p=cartagena.git;a=commitdiff_plain;h=81da7fe341fd14c69c06419328a403c36422a054 Initial commit --- 81da7fe341fd14c69c06419328a403c36422a054 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 0000000..8630fab Binary files /dev/null and b/lib/html/.svn/text-base/blue-pirate.gif.svn-base differ 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 0000000..1625856 Binary files /dev/null and b/lib/html/.svn/text-base/blue_pirate.png.svn-base differ 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 0000000..849efa0 Binary files /dev/null and b/lib/html/.svn/text-base/boat.gif.svn-base differ 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 0000000..a471eb1 Binary files /dev/null and b/lib/html/.svn/text-base/boat.png.svn-base differ diff --git a/lib/html/.svn/text-base/bottle.jpg.svn-base b/lib/html/.svn/text-base/bottle.jpg.svn-base new file mode 100644 index 0000000..f030006 Binary files /dev/null and b/lib/html/.svn/text-base/bottle.jpg.svn-base differ 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 0000000..1061015 Binary files /dev/null and b/lib/html/.svn/text-base/bottle.png.svn-base differ 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 0000000..2db25e9 Binary files /dev/null and b/lib/html/.svn/text-base/brown_pirate.png.svn-base differ diff --git a/lib/html/.svn/text-base/cell.png.svn-base b/lib/html/.svn/text-base/cell.png.svn-base new file mode 100644 index 0000000..bc7e2a0 Binary files /dev/null and b/lib/html/.svn/text-base/cell.png.svn-base differ 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 0000000..e8e1f37 Binary files /dev/null and b/lib/html/.svn/text-base/dagger.jpg.svn-base differ 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 0000000..0d4dabf Binary files /dev/null and b/lib/html/.svn/text-base/dagger.png.svn-base differ diff --git a/lib/html/.svn/text-base/green_pirate.png.svn-base b/lib/html/.svn/text-base/green_pirate.png.svn-base new file mode 100644 index 0000000..89223d3 Binary files /dev/null and b/lib/html/.svn/text-base/green_pirate.png.svn-base differ diff --git a/lib/html/.svn/text-base/gun.jpg.svn-base b/lib/html/.svn/text-base/gun.jpg.svn-base new file mode 100644 index 0000000..bc8979e Binary files /dev/null and b/lib/html/.svn/text-base/gun.jpg.svn-base differ 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 0000000..b8c8237 Binary files /dev/null and b/lib/html/.svn/text-base/gun.png.svn-base differ diff --git a/lib/html/.svn/text-base/hat.jpg.svn-base b/lib/html/.svn/text-base/hat.jpg.svn-base new file mode 100644 index 0000000..fe8e99b Binary files /dev/null and b/lib/html/.svn/text-base/hat.jpg.svn-base differ 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 0000000..94a7ad5 Binary files /dev/null and b/lib/html/.svn/text-base/hat.png.svn-base differ 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 0000000..a0f655e Binary files /dev/null and b/lib/html/.svn/text-base/keys.jpg.svn-base differ 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 0000000..df6147f Binary files /dev/null and b/lib/html/.svn/text-base/keys.png.svn-base differ diff --git a/lib/html/.svn/text-base/red_pirate.png.svn-base b/lib/html/.svn/text-base/red_pirate.png.svn-base new file mode 100644 index 0000000..9668047 Binary files /dev/null and b/lib/html/.svn/text-base/red_pirate.png.svn-base differ diff --git a/lib/html/.svn/text-base/retreat.png.svn-base b/lib/html/.svn/text-base/retreat.png.svn-base new file mode 100644 index 0000000..09ad452 Binary files /dev/null and b/lib/html/.svn/text-base/retreat.png.svn-base differ diff --git a/lib/html/.svn/text-base/rules.html.svn-base b/lib/html/.svn/text-base/rules.html.svn-base new file mode 100644 index 0000000..876e2b1 --- /dev/null +++ b/lib/html/.svn/text-base/rules.html.svn-base @@ -0,0 +1,53 @@ + + +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 0000000..0575e00 Binary files /dev/null and b/lib/html/.svn/text-base/skull.jpg.svn-base differ diff --git a/lib/html/.svn/text-base/skull.png.svn-base b/lib/html/.svn/text-base/skull.png.svn-base new file mode 100644 index 0000000..cad84e3 Binary files /dev/null and b/lib/html/.svn/text-base/skull.png.svn-base differ 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 0000000..b697834 Binary files /dev/null and b/lib/html/.svn/text-base/yellow_pirate.png.svn-base differ diff --git a/lib/html/blue-pirate.gif b/lib/html/blue-pirate.gif new file mode 100644 index 0000000..8630fab Binary files /dev/null and b/lib/html/blue-pirate.gif differ diff --git a/lib/html/blue_pirate.png b/lib/html/blue_pirate.png new file mode 100644 index 0000000..1625856 Binary files /dev/null and b/lib/html/blue_pirate.png differ diff --git a/lib/html/boat.gif b/lib/html/boat.gif new file mode 100644 index 0000000..849efa0 Binary files /dev/null and b/lib/html/boat.gif differ diff --git a/lib/html/boat.png b/lib/html/boat.png new file mode 100644 index 0000000..a471eb1 Binary files /dev/null and b/lib/html/boat.png differ diff --git a/lib/html/bottle.jpg b/lib/html/bottle.jpg new file mode 100644 index 0000000..f030006 Binary files /dev/null and b/lib/html/bottle.jpg differ diff --git a/lib/html/bottle.png b/lib/html/bottle.png new file mode 100644 index 0000000..1061015 Binary files /dev/null and b/lib/html/bottle.png differ diff --git a/lib/html/brown_pirate.png b/lib/html/brown_pirate.png new file mode 100644 index 0000000..2db25e9 Binary files /dev/null and b/lib/html/brown_pirate.png differ diff --git a/lib/html/cell.png b/lib/html/cell.png new file mode 100644 index 0000000..bc7e2a0 Binary files /dev/null and b/lib/html/cell.png differ 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 0000000..e8e1f37 Binary files /dev/null and b/lib/html/dagger.jpg differ diff --git a/lib/html/dagger.png b/lib/html/dagger.png new file mode 100644 index 0000000..0d4dabf Binary files /dev/null and b/lib/html/dagger.png differ diff --git a/lib/html/green_pirate.png b/lib/html/green_pirate.png new file mode 100644 index 0000000..89223d3 Binary files /dev/null and b/lib/html/green_pirate.png differ diff --git a/lib/html/gun.jpg b/lib/html/gun.jpg new file mode 100644 index 0000000..bc8979e Binary files /dev/null and b/lib/html/gun.jpg differ diff --git a/lib/html/gun.png b/lib/html/gun.png new file mode 100644 index 0000000..b8c8237 Binary files /dev/null and b/lib/html/gun.png differ diff --git a/lib/html/hat.jpg b/lib/html/hat.jpg new file mode 100644 index 0000000..fe8e99b Binary files /dev/null and b/lib/html/hat.jpg differ diff --git a/lib/html/hat.png b/lib/html/hat.png new file mode 100644 index 0000000..94a7ad5 Binary files /dev/null and b/lib/html/hat.png differ 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 0000000..a0f655e Binary files /dev/null and b/lib/html/keys.jpg differ diff --git a/lib/html/keys.png b/lib/html/keys.png new file mode 100644 index 0000000..df6147f Binary files /dev/null and b/lib/html/keys.png differ diff --git a/lib/html/red_pirate.png b/lib/html/red_pirate.png new file mode 100644 index 0000000..9668047 Binary files /dev/null and b/lib/html/red_pirate.png differ diff --git a/lib/html/retreat.png b/lib/html/retreat.png new file mode 100644 index 0000000..09ad452 Binary files /dev/null and b/lib/html/retreat.png differ diff --git a/lib/html/robots/.svn/all-wcprops b/lib/html/robots/.svn/all-wcprops new file mode 100644 index 0000000..7f10177 --- /dev/null +++ b/lib/html/robots/.svn/all-wcprops @@ -0,0 +1,5 @@ +K 25 +svn:wc:ra_dav:version-url +V 53 +/svn/njae/!svn/ver/43/cartagena/trunk/lib/html/robots +END diff --git a/lib/html/robots/.svn/entries b/lib/html/robots/.svn/entries new file mode 100644 index 0000000..eb63fab --- /dev/null +++ b/lib/html/robots/.svn/entries @@ -0,0 +1,34 @@ +8 + +dir +65 +http://scripts.njae.me.uk/svn/njae/cartagena/trunk/lib/html/robots +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 + +1 +dir + +2 +dir + diff --git a/lib/html/robots/.svn/format b/lib/html/robots/.svn/format new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/lib/html/robots/.svn/format @@ -0,0 +1 @@ +8 diff --git a/lib/html/robots/1/.svn/all-wcprops b/lib/html/robots/1/.svn/all-wcprops new file mode 100644 index 0000000..287b1c3 --- /dev/null +++ b/lib/html/robots/1/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 55 +/svn/njae/!svn/ver/42/cartagena/trunk/lib/html/robots/1 +END +runme.rb +K 25 +svn:wc:ra_dav:version-url +V 64 +/svn/njae/!svn/ver/42/cartagena/trunk/lib/html/robots/1/runme.rb +END diff --git a/lib/html/robots/1/.svn/entries b/lib/html/robots/1/.svn/entries new file mode 100644 index 0000000..2ebdd92 --- /dev/null +++ b/lib/html/robots/1/.svn/entries @@ -0,0 +1,27 @@ +8 + +dir +65 +http://scripts.njae.me.uk/svn/njae/cartagena/trunk/lib/html/robots/1 +http://scripts.njae.me.uk/svn/njae + + + +2008-10-16T20:38:07.769887Z +42 + + + +svn:special svn:externals svn:needs-lock + +runme.rb +file + + + + +2008-10-16T14:42:29.000000Z +db3d894459b82c2f69b1ddd6b5d66858 +2008-10-16T20:38:07.769887Z +42 + diff --git a/lib/html/robots/1/.svn/format b/lib/html/robots/1/.svn/format new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/lib/html/robots/1/.svn/format @@ -0,0 +1 @@ +8 diff --git a/lib/html/robots/1/.svn/text-base/runme.rb.svn-base b/lib/html/robots/1/.svn/text-base/runme.rb.svn-base new file mode 100644 index 0000000..4c96457 --- /dev/null +++ b/lib/html/robots/1/.svn/text-base/runme.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/html/robots/1/runme.rb b/lib/html/robots/1/runme.rb new file mode 100644 index 0000000..4c96457 --- /dev/null +++ b/lib/html/robots/1/runme.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/html/robots/2/.svn/all-wcprops b/lib/html/robots/2/.svn/all-wcprops new file mode 100644 index 0000000..9d478f7 --- /dev/null +++ b/lib/html/robots/2/.svn/all-wcprops @@ -0,0 +1,11 @@ +K 25 +svn:wc:ra_dav:version-url +V 55 +/svn/njae/!svn/ver/43/cartagena/trunk/lib/html/robots/2 +END +runme.rb +K 25 +svn:wc:ra_dav:version-url +V 64 +/svn/njae/!svn/ver/43/cartagena/trunk/lib/html/robots/2/runme.rb +END diff --git a/lib/html/robots/2/.svn/entries b/lib/html/robots/2/.svn/entries new file mode 100644 index 0000000..7f845d7 --- /dev/null +++ b/lib/html/robots/2/.svn/entries @@ -0,0 +1,39 @@ +8 + +dir +65 +http://scripts.njae.me.uk/svn/njae/cartagena/trunk/lib/html/robots/2 +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 + +runme.rb +file + + + + +2008-10-17T07:28:50.000000Z +9d7aa6b67c047dd18ff05819c78e9a7d +2008-10-17T07:57:36.358504Z +43 + diff --git a/lib/html/robots/2/.svn/format b/lib/html/robots/2/.svn/format new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/lib/html/robots/2/.svn/format @@ -0,0 +1 @@ +8 diff --git a/lib/html/robots/2/.svn/text-base/runme.rb.svn-base b/lib/html/robots/2/.svn/text-base/runme.rb.svn-base new file mode 100644 index 0000000..53f6c5f --- /dev/null +++ b/lib/html/robots/2/.svn/text-base/runme.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/html/robots/2/runme.rb b/lib/html/robots/2/runme.rb new file mode 100644 index 0000000..53f6c5f --- /dev/null +++ b/lib/html/robots/2/runme.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/lib/html/rules.html b/lib/html/rules.html new file mode 100644 index 0000000..876e2b1 --- /dev/null +++ b/lib/html/rules.html @@ -0,0 +1,53 @@ + + +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 0000000..0575e00 Binary files /dev/null and b/lib/html/skull.jpg differ diff --git a/lib/html/skull.png b/lib/html/skull.png new file mode 100644 index 0000000..cad84e3 Binary files /dev/null and b/lib/html/skull.png differ diff --git a/lib/html/yellow_pirate.png b/lib/html/yellow_pirate.png new file mode 100644 index 0000000..b697834 Binary files /dev/null and b/lib/html/yellow_pirate.png differ diff --git a/lib/libcartagena.rb b/lib/libcartagena.rb new file mode 100644 index 0000000..a0574d8 --- /dev/null +++ b/lib/libcartagena.rb @@ -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/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