threesmodel 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 957115b434fa33462e405bee92fa5d888674a0ca
4
+ data.tar.gz: 5b7a6860503769c440c8218678f4324bb793a033
5
+ SHA512:
6
+ metadata.gz: fe6510bb2b3bb1336fdb24e9d1e7e36e1cc71e522ffc6aec469ecf9d276c12f41a825e428346a3c92d2ab68ae2ba940b391b8330eef7dcf176707360bf09d9bd
7
+ data.tar.gz: 231be835a76ee3e4ba4e8c091f556ade7611abeccab1db1216970bcf1920d797e084d5d24643b756bdd28ea859f20f5080082752a8893e08f9d5836a4d541f3a
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ reports/
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,11 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in threesmodel.gemspec
4
+ gemspec
5
+
6
+ gem 'rspec'
7
+ gem 'rspec-mocks'
8
+ gem 'cucumber'
9
+ gem 'fivemat'
10
+ gem 'fuubar'
11
+ gem 'histogram'
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Måns Sandström
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # Threesmodel
2
+
3
+ This is an attempt at emulating the game model of the excellent game "Threes".
4
+ It is implemented in Ruby and specified with Gehrkin and rspec examples.
5
+ The gem is not indended to be a game in it self, but with relative ease one
6
+ could use it as the game engine for a command line or a web version of the game.
7
+ It supports playing multiple games simultaneously.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'threesmodel'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install threesmodel
22
+
23
+ ## Usage
24
+
25
+ Creating and playing a basic game works like this:
26
+
27
+ require 'threesmodel'
28
+ game_controller = Threesmodel::GameController.new
29
+ game_state = game_controller.start_new_game
30
+
31
+ The game state object is a hash with the keys :id (the identifier of the game),
32
+ :game the layout of the current game board (as an instance of a Matrix class),
33
+ :game_over (boolean that denotes whether the game is over or not),
34
+ :next_number states the number to be added after the next fold and
35
+ :score that states the current score.
36
+
37
+ Play by issuing fold calls to the game controller identifying the game with the id like this:
38
+
39
+ game_state = game_controller.fold_left(game_state[:id])
40
+ game_state = game_controller.fold_right(game_state[:id])
41
+ game_state = game_controller.fold_up(game_state[:id])
42
+ game_state = game_controller.fold_down(game_state[:id])
43
+
44
+
45
+ ## Contributing
46
+
47
+ 1. Fork it
48
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
49
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
50
+ 4. Push to the branch (`git push origin my-new-feature`)
51
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,28 @@
1
+ require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+ require 'cucumber/rake/task'
4
+
5
+ Cucumber::Rake::Task.new(:cucumber) do |t|
6
+
7
+ #t.cucumber_opts = '--format Fivemat'
8
+ end
9
+
10
+ RSpec::Core::RakeTask.new(:spec) do |t|
11
+ t.rspec_opts = '--format Fivemat'
12
+ end
13
+
14
+ RSpec::Core::RakeTask.new(:htmlspec) do |t|
15
+ t.rspec_opts = '--format html --out reports/rspec_results.html'
16
+ end
17
+
18
+ task :default => :spec
19
+
20
+ task :test => [:cucumber, :spec]
21
+
22
+ namespace :rspec_report do
23
+ desc 'Run all specs, generate RSpec report and open it in the browser'
24
+ task :browser do
25
+ Rake::Task[:htmlspec].invoke
26
+ `open reports/rspec_results.html` # This only works if running OS X.
27
+ end
28
+ end
@@ -0,0 +1,66 @@
1
+ Feature: The start situation is playable and 1's fold into 2's to create 3's.
2
+ Scenario: I have just started a game. The board is full of small numbers and empty cells.
3
+
4
+ Given a gameboard
5
+ |0|1|2|0|
6
+ |0|1|2|0|
7
+ |0|1|2|0|
8
+ |0|1|2|0|
9
+ When the game is folded right to left
10
+ Then the gameboard looks like
11
+ |1|2|0|0|
12
+ |1|2|0|0|
13
+ |1|2|0|0|
14
+ |1|2|0|0|
15
+
16
+ Scenario: I am folding ones and twos in a fairly empty board
17
+ Given a gameboard
18
+ |1|2|0|0|
19
+ |1|2|0|0|
20
+ |1|2|0|0|
21
+ |1|2|0|0|
22
+ When the game is folded right to left
23
+ Then the gameboard looks like
24
+ |3|0|0|0|
25
+ |3|0|0|0|
26
+ |3|0|0|0|
27
+ |3|0|0|0|
28
+
29
+ Scenario: I am folding right over zeros
30
+ Given a gameboard
31
+ |1|2|0|0|
32
+ |1|2|0|0|
33
+ |1|2|0|0|
34
+ |1|2|0|0|
35
+ When the game is folded left to right
36
+ Then the gameboard looks like
37
+ |0|1|2|0|
38
+ |0|1|2|0|
39
+ |0|1|2|0|
40
+ |0|1|2|0|
41
+
42
+ Scenario: I am folding up over zeros
43
+ Given a gameboard
44
+ |0|0|0|0|
45
+ |2|2|2|2|
46
+ |1|1|1|1|
47
+ |0|0|0|0|
48
+ When the game is folded upwards
49
+ Then the gameboard looks like
50
+ |2|2|2|2|
51
+ |1|1|1|1|
52
+ |0|0|0|0|
53
+ |0|0|0|0|
54
+
55
+ Scenario: I am folding down over zeros
56
+ Given a gameboard
57
+ |0|0|0|0|
58
+ |2|2|2|2|
59
+ |1|1|1|1|
60
+ |0|0|0|0|
61
+ When the game is folded downwards
62
+ Then the gameboard looks like
63
+ |0|0|0|0|
64
+ |0|0|0|0|
65
+ |2|2|2|2|
66
+ |1|1|1|1|
@@ -0,0 +1,82 @@
1
+ Feature: The start situation is playable and equal numbers fold to create new, double numbers.
2
+ Scenario: Threes will fold into sixes.
3
+ Given a gameboard
4
+ |0| 0|0|0|
5
+ |0| 0|0|0|
6
+ |3| 6|0|0|
7
+ |3|12|2|0|
8
+ When the game is folded downwards
9
+ Then the gameboard looks like
10
+ |0| 0|0|0|
11
+ |0| 0|0|0|
12
+ |0| 6|0|0|
13
+ |6|12|2|0|
14
+
15
+ Scenario: Equal numbers in several lines
16
+ Given a gameboard
17
+ |1|2| 0| 0|
18
+ |1|2| 0| 0|
19
+ |3|6|12|24|
20
+ |3|6|12|24|
21
+ When the game is folded downwards
22
+ Then the gameboard looks like
23
+ |0| 0| 0| 0|
24
+ |1| 2| 0| 0|
25
+ |1| 2| 0| 0|
26
+ |6|12|24|48|
27
+
28
+ Scenario: Equal numbers in the middle of the line
29
+ Given a gameboard
30
+ |6|2| 3| 0|
31
+ |6|3|12|24|
32
+ |6|3|12|24|
33
+ |3|6| 3| 3|
34
+ When the game is folded downwards
35
+ Then the gameboard looks like
36
+ | 0|0| 0| 0|
37
+ | 6|2| 3| 0|
38
+ |12|6|24|48|
39
+ | 3|6| 3| 3|
40
+
41
+ Scenario: Fold an unanimous matrix completely
42
+ Given a gameboard
43
+ |12|12|12|12|
44
+ |12|12|12|12|
45
+ |12|12|12|12|
46
+ |12|12|12|12|
47
+ When the game is folded downwards
48
+ Then the gameboard looks like
49
+ | 0| 0| 0| 0|
50
+ |12|12|12|12|
51
+ |12|12|12|12|
52
+ |24|24|24|24|
53
+ When the game is folded downwards
54
+ Then the gameboard looks like
55
+ | 0| 0| 0| 0|
56
+ | 0| 0| 0| 0|
57
+ |24|24|24|24|
58
+ |24|24|24|24|
59
+ When the game is folded downwards
60
+ Then the gameboard looks like
61
+ | 0| 0| 0| 0|
62
+ | 0| 0| 0| 0|
63
+ | 0| 0| 0| 0|
64
+ |48|48|48|48|
65
+ When the game is folded right
66
+ Then the gameboard looks like
67
+ |0| 0| 0| 0|
68
+ |0| 0| 0| 0|
69
+ |0| 0| 0| 0|
70
+ |0|48|48|96|
71
+ When the game is folded right
72
+ Then the gameboard looks like
73
+ |0|0| 0| 0|
74
+ |0|0| 0| 0|
75
+ |0|0| 0| 0|
76
+ |0|0|96|96|
77
+ When the game is folded right
78
+ Then the gameboard looks like
79
+ |0|0|0| 0|
80
+ |0|0|0| 0|
81
+ |0|0|0| 0|
82
+ |0|0|0|192|
@@ -0,0 +1,18 @@
1
+ Feature: A game ends when there is no foldable line.
2
+ Scenario: An unfoldable game board means that the game is over.
3
+ Given an unfoldable game board
4
+ Then the game is over
5
+
6
+ Scenario: A foldable game board means that the game continues.
7
+ Given a foldable game board
8
+ Then the game continues
9
+
10
+ Scenario: A game board that folds in only one direction means that the game continues.
11
+ Given a game board that only folds left and up
12
+ Then the game continues
13
+ Given a game board that only folds left and down
14
+ Then the game continues
15
+ Given a game board that only folds right and up
16
+ Then the game continues
17
+ Given a game board that only folds right and down
18
+ Then the game continues
@@ -0,0 +1,112 @@
1
+ Feature: For every game state there is a score.
2
+ Scenario: Ones and twos give zero points.
3
+ Given a gameboard
4
+ |0|0|0|1|
5
+ |0|1|2|0|
6
+ |2|1|0|2|
7
+ |1|2|1|0|
8
+ Then score is:"0"
9
+
10
+ Scenario: A three gives 3 points
11
+ Given a gameboard
12
+ |0|0|0|1|
13
+ |0|1|2|0|
14
+ |2|1|0|2|
15
+ |1|2|1|3|
16
+ Then score is:"3"
17
+
18
+ Scenario: A six gives 9 points
19
+ Given a gameboard
20
+ |0|0|0|1|
21
+ |0|1|2|0|
22
+ |2|1|0|2|
23
+ |1|2|1|6|
24
+ Then score is:"9"
25
+
26
+ Scenario: A 12 gives 27 points
27
+ Given a gameboard
28
+ |0|0|0| 1|
29
+ |0|1|2| 0|
30
+ |2|1|0| 2|
31
+ |1|2|1|12|
32
+ Then score is:"27"
33
+
34
+ Scenario: A 24 gives 81 points
35
+ Given a gameboard
36
+ |0|0|0| 1|
37
+ |0|1|2| 0|
38
+ |2|1|0| 2|
39
+ |1|2|1|24|
40
+ Then score is:"81"
41
+
42
+ Scenario: A 48 gives 243 points
43
+ Given a gameboard
44
+ |0|0|0| 1|
45
+ |0|1|2| 0|
46
+ |2|1|0| 2|
47
+ |1|2|1|48|
48
+ Then score is:"243"
49
+
50
+ Scenario: A 96 gives 729 points
51
+ Given a gameboard
52
+ |0|0|0| 1|
53
+ |0|1|2| 0|
54
+ |2|1|0| 2|
55
+ |1|2|1|96|
56
+ Then score is:"729"
57
+
58
+ Scenario: A 192 gives 2187 points
59
+ Given a gameboard
60
+ |0|0|0| 1|
61
+ |0|1|2| 0|
62
+ |2|1|0| 2|
63
+ |1|2|1|192|
64
+ Then score is:"2187"
65
+
66
+ Scenario: A 384 gives 6561 points
67
+ Given a gameboard
68
+ |0|0|0| 1|
69
+ |0|1|2| 0|
70
+ |2|1|0| 2|
71
+ |1|2|1|384|
72
+ Then score is:"6561"
73
+
74
+ Scenario: A 768 gives 19683 points
75
+ Given a gameboard
76
+ |0|0|0| 1|
77
+ |0|1|2| 0|
78
+ |2|1|0| 2|
79
+ |1|2|1|768|
80
+ Then score is:"19683"
81
+
82
+ Scenario: A 1536 gives 59049 points
83
+ Given a gameboard
84
+ |0|0|0| 1|
85
+ |0|1|2| 0|
86
+ |2|1|0| 2|
87
+ |1|2|1|1536|
88
+ Then score is:"59049"
89
+
90
+ Scenario: A 3072 gives 177147 points
91
+ Given a gameboard
92
+ |0|0|0| 1|
93
+ |0|1|2| 0|
94
+ |2|1|0| 2|
95
+ |1|2|1|3072|
96
+ Then score is:"177147"
97
+
98
+ Scenario: A 6144 gives 531441 points
99
+ Given a gameboard
100
+ |0|0|0| 1|
101
+ |0|1|2| 0|
102
+ |2|1|0| 2|
103
+ |1|2|1|6144|
104
+ Then score is:"531441"
105
+
106
+ Scenario: All points are summed up
107
+ Given a gameboard
108
+ | 1| 2| 3| 6|
109
+ | 12| 24| 48| 96|
110
+ | 192| 384|768|1536|
111
+ |3072|6144| 0| 0|
112
+ Then score is:"797160"
@@ -0,0 +1,14 @@
1
+ Feature: Start a new game.
2
+ Scenario: A newly created game is not over before first fold.
3
+ Given a new game is started
4
+ Then the game is not over
5
+
6
+ Scenario: A newly created game is identified by an id.
7
+ Given a new game is started
8
+ Then an id is attached to the game
9
+
10
+ Scenario: A newly created game returns a game board with nine cells filled.
11
+ Given a new game is started
12
+ Then the game board has 9 cells filled
13
+ And the board contains no other but the following numbers:
14
+ |0|1|2|3|
@@ -0,0 +1,41 @@
1
+ require 'Matrix'
2
+ require_relative '../../lib/game_board_folder'
3
+ require_relative '../../lib/score_calculator'
4
+
5
+ Given(/^a gameboard$/) do |table|
6
+ @game_board = GameBoardFolder.new
7
+ matrix = table.raw
8
+ matrix = matrix.map{|l| l.map{|cell| cell.strip.to_i}}
9
+ @state = Matrix.rows(matrix)
10
+ end
11
+
12
+ When(/^the game is folded right to left$/) do
13
+ @state = @game_board.fold_left @state
14
+ end
15
+
16
+ When(/^the game is folded left to right$/) do
17
+ @state = @game_board.fold_right @state
18
+ end
19
+
20
+ When(/^the game is folded upwards$/) do
21
+ @state = @game_board.fold_up @state
22
+ end
23
+
24
+ When(/^the game is folded downwards$/) do
25
+ @state = @game_board.fold_down @state
26
+ end
27
+
28
+ When(/^the game is folded right$/) do
29
+ @state = @game_board.fold_right @state
30
+ end
31
+
32
+ Then(/^the gameboard looks like$/) do |table|
33
+ matrix = table.raw
34
+ matrix = matrix.map{|l| l.map{|cell| cell.strip.to_i}}
35
+ expect(@state).to eq(Matrix.rows(matrix))
36
+ end
37
+
38
+ Then(/^score is:"(.*?)"$/) do |score|
39
+ actual_score = ScoreCalculator.score_for(@state)
40
+ expect(actual_score).to eq(score.to_i)
41
+ end
@@ -0,0 +1,44 @@
1
+ require 'Matrix'
2
+ require_relative '../../lib/game_over_checker'
3
+
4
+ Before do
5
+ @game_over_checker = GameOverChecker.new
6
+ end
7
+
8
+ Given(/^an unfoldable game board$/) do
9
+ matrix = [[3,6,3,6],[6,3,6,3],[3,6,3,6],[6,3,6,3]]
10
+ @state = Matrix.rows(matrix)
11
+ end
12
+
13
+ Given(/^a game board that only folds left and up$/) do
14
+ matrix = [[0,6,3,6],[6,3,6,3],[3,6,3,6],[6,3,6,3]]
15
+ @state = Matrix.rows(matrix)
16
+ end
17
+
18
+ Given(/^a game board that only folds left and down$/) do
19
+ matrix = [[3,6,3,6],[6,3,6,3],[3,6,3,6],[0,3,6,3]]
20
+ @state = Matrix.rows(matrix)
21
+ end
22
+
23
+ Given(/^a game board that only folds right and up$/) do
24
+ matrix = [[3,6,3,0],[6,3,6,3],[3,6,3,6],[6,3,6,3]]
25
+ @state = Matrix.rows(matrix)
26
+ end
27
+
28
+ Given(/^a game board that only folds right and down$/) do
29
+ matrix = [[3,6,3,6],[6,3,6,3],[3,6,3,6],[6,3,6,0]]
30
+ @state = Matrix.rows(matrix)
31
+ end
32
+
33
+ Then(/^the game is over$/) do
34
+ expect(@game_over_checker.game_over?(@state)).to eq(true)
35
+ end
36
+
37
+ Given(/^a foldable game board$/) do
38
+ matrix = [[3,3,3,3],[3,3,3,3],[3,3,3,3],[3,3,3,3]]
39
+ @state = Matrix.rows(matrix)
40
+ end
41
+
42
+ Then(/^the game continues$/) do
43
+ expect(@game_over_checker.game_over?(@state)).to eq(false)
44
+ end
@@ -0,0 +1,39 @@
1
+ require_relative '../../lib/threesmodel'
2
+
3
+ Before do
4
+ end
5
+
6
+ Given(/^a new game is started$/) do
7
+ @game_controller = Threesmodel::GameController.new
8
+ @game_state = @game_controller.start_new_game
9
+ end
10
+
11
+ Then(/^an id is attached to the game$/) do
12
+ expect(@game_state[:id]).to_not eq(nil)
13
+ end
14
+
15
+ Then(/^the game is not over$/) do
16
+ expect(@game_state[:game_over]).to eq(false)
17
+ end
18
+
19
+ Then(/^the game board has (\d+) cells filled$/) do |filled_cells|
20
+ c = []
21
+ values = @game_state[:game].row_vectors.each{|row|
22
+ c << row.to_a
23
+ }
24
+ c.flatten!
25
+ d = c.find_all{|item| item > 0}
26
+ expect(d.size).to eq(9)
27
+ end
28
+
29
+ Then(/^the board contains no other but the following numbers:$/) do |table|
30
+ data = table.raw[0]
31
+ c = []
32
+ values = @game_state[:game].row_vectors.each{|row|
33
+ c << row.to_a
34
+ }
35
+ c.flatten!.uniq!
36
+ data.each{|value|
37
+ expect(c.include?(value.to_i)).to eq(true)
38
+ }
39
+ end
@@ -0,0 +1,47 @@
1
+ require 'line_folder'
2
+ class CandidateExtractor
3
+
4
+ def initialize(line_folder = LineFolder.new)
5
+ @line_folder = line_folder
6
+ end
7
+
8
+ def fold_left_candidates(state)
9
+ candidates = []
10
+ state.row_vectors.each_index {|i|
11
+ add_candidate(candidates, i, state.row_vectors[i].to_a)
12
+ }
13
+ candidates
14
+ end
15
+
16
+ def fold_right_candidates(state)
17
+ candidates = []
18
+ state.row_vectors.each_index {|i|
19
+ add_candidate(candidates, i, state.row_vectors[i].to_a.reverse)
20
+ }
21
+ candidates
22
+ end
23
+
24
+ def fold_up_candidates(state)
25
+ candidates = []
26
+ state.column_vectors.each_index {|i|
27
+ add_candidate(candidates, i, state.column_vectors[i].to_a)
28
+ }
29
+ candidates
30
+ end
31
+
32
+ def fold_down_candidates(state)
33
+ candidates = []
34
+ state.column_vectors.each_index {|i|
35
+ add_candidate(candidates, i, state.column_vectors[i].to_a.reverse)
36
+ }
37
+ candidates
38
+ end
39
+
40
+ def add_candidate(candidates, index, values)
41
+ if (@line_folder.can_fold?(values)) then
42
+ candidates << [index, 3]
43
+ end
44
+ end
45
+
46
+
47
+ end
@@ -0,0 +1,26 @@
1
+ class CandidateTranslator
2
+
3
+ def translate_left_fold(candidates)
4
+ candidates
5
+ end
6
+
7
+ def translate_right_fold(candidates)
8
+ translate(candidates, {[0,3] => [3,0], [1,3] => [2,0], [2,3] => [1,0], [3,3] => [0,0]})
9
+ end
10
+
11
+ def translate_down_fold(candidates)
12
+ translate(candidates, {[0,3] => [0,0], [1,3] => [0,1], [2,3] => [0,2], [3,3] => [0,3]})
13
+ end
14
+
15
+ def translate_up_fold(candidates)
16
+ translate(candidates, {[0,3] => [3,3], [1,3] => [3,2], [2,3] => [3,1], [3,3] => [3,0]})
17
+ end
18
+
19
+ def translate(candidates, translation)
20
+ result = []
21
+ for candidate in candidates do
22
+ result << translation[candidate]
23
+ end
24
+ result
25
+ end
26
+ end