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.
@@ -0,0 +1,86 @@
1
+ require_relative 'line_folder'
2
+ require "Matrix"
3
+ # A game board keeps and manipulates the state of the game.
4
+ class GameBoardFolder
5
+
6
+ def initialize(line_folder = LineFolder.new )
7
+ @line_folder = line_folder
8
+ end
9
+
10
+ # Folds the game board from right to left.
11
+ def fold_left(state)
12
+ new_state = []
13
+ state.row_vectors.each_index {|i|
14
+ values = state.row_vectors[i].to_a
15
+ values = @line_folder.fold(values)
16
+ new_state << values
17
+ }
18
+ Matrix.rows(new_state)
19
+ end
20
+
21
+ # Folds the game board from left to right.
22
+ def fold_right(state)
23
+ new_state = []
24
+ state.row_vectors.each {|row|
25
+ values = row.to_a.reverse
26
+ values = @line_folder.fold(values)
27
+ new_state << values.reverse
28
+ }
29
+ Matrix.rows(new_state)
30
+ end
31
+
32
+ # Folds the game board upwards.
33
+ def fold_up(state)
34
+ new_state = []
35
+ state.column_vectors.each {|column|
36
+ values = column.to_a
37
+ values = @line_folder.fold(values)
38
+ new_state << values
39
+ }
40
+ Matrix.columns(new_state)
41
+ end
42
+
43
+ # Folds the game board downwards.
44
+ def fold_down(state)
45
+ new_state = []
46
+ state.column_vectors.each {|column|
47
+ values = column.to_a.reverse
48
+ values = @line_folder.fold(values)
49
+ new_state << values.reverse
50
+ }
51
+ Matrix.columns(new_state)
52
+ end
53
+
54
+ def can_fold_left?(state)
55
+ foldable = false
56
+ state.row_vectors.each{|line|
57
+ foldable = (foldable or @line_folder.can_fold?(line.to_a))
58
+ }
59
+ foldable
60
+ end
61
+
62
+ def can_fold_right?(state)
63
+ foldable = false
64
+ state.row_vectors.each{|line|
65
+ foldable = (foldable or @line_folder.can_fold?(line.to_a.reverse))
66
+ }
67
+ foldable
68
+ end
69
+
70
+ def can_fold_up?(state)
71
+ foldable = false
72
+ state.column_vectors.each{|line|
73
+ foldable = (foldable or @line_folder.can_fold?(line.to_a))
74
+ }
75
+ foldable
76
+ end
77
+
78
+ def can_fold_down?(state)
79
+ foldable = false
80
+ state.column_vectors.each{|line|
81
+ foldable = (foldable or @line_folder.can_fold?(line.to_a.reverse))
82
+ }
83
+ foldable
84
+ end
85
+
86
+ end
@@ -0,0 +1,30 @@
1
+ require 'Matrix'
2
+
3
+ class GameCreator
4
+ def self.create_new_game
5
+ positions = random_positions(9)
6
+ create_matrix(positions)
7
+ end
8
+
9
+ def self.create_matrix(positions)
10
+ rows = []
11
+ 4.times {
12
+ rows << [0,0,0,0]
13
+ }
14
+ positions.each {|position|
15
+ rows[position[0]][position[1]] = rand(1..3)
16
+ }
17
+ Matrix.rows(rows)
18
+ end
19
+
20
+ def self.random_positions(number_of_positions)
21
+ positions = []
22
+ while (positions.length < number_of_positions)
23
+ candidate_position = [rand(0..3), rand(0..3)]
24
+ unless positions.include?(candidate_position)
25
+ positions << candidate_position
26
+ end
27
+ end
28
+ positions
29
+ end
30
+ end
@@ -0,0 +1,18 @@
1
+ require_relative 'line_folder'
2
+ class GameOverChecker
3
+
4
+ def initialize
5
+ @line_folder = LineFolder.new
6
+ end
7
+
8
+ def game_over?(state)
9
+ state.row_vectors.each {|row|
10
+ if ((@line_folder.can_fold?(row.to_a)) or (@line_folder.can_fold?(row.to_a.reverse))) then return false end
11
+ }
12
+ state.column_vectors.each{|column|
13
+ if ((@line_folder.can_fold?(column.to_a)) or (@line_folder.can_fold?(column.to_a.reverse))) then return false end
14
+ }
15
+ true
16
+ end
17
+
18
+ end
@@ -0,0 +1,93 @@
1
+ # The LineFolder can fold an array with four cells according to the rules of
2
+ # threes.
3
+ class LineFolder
4
+
5
+ def initialize
6
+ Array.class_eval do
7
+ # Determines the "wall" position and the collapses the first to collapsible cells inserting a zero at the end. Assumes a foldable line.
8
+ def squash
9
+ array = Array.new(self)
10
+ wall_index = wall_index_of
11
+ array[wall_index] = array[wall_index] + array[wall_index + 1]
12
+ while wall_index < 2
13
+ array[wall_index + 1] = array[wall_index + 2]
14
+ wall_index += 1
15
+ end
16
+ array[3] = 0
17
+ return array
18
+ end
19
+
20
+ def wall_index_of
21
+ return 0 if self[0] == 0
22
+ return 0 if self[0] == 1 and self[1] == 2
23
+ return 0 if self[0] == 2 and self[1] == 1
24
+ if self[0] > 2
25
+ return 0 if self[0] == self[1]
26
+ end
27
+ return 1 if self[1] == 0
28
+ return 1 if (self[0] != self[1]) and self[1] == 1 and self[2] == 2
29
+ return 1 if (self[0] != self[1]) and self[1] == 2 and self[2] == 1
30
+ return 1 if (self[0] == self[1]) and self[1] == 1 and self[2] == 2
31
+ return 1 if (self[0] == self[1]) and self[1] == 2 and self[2] == 1
32
+ if self[1] > 2
33
+ return 1 if self[1] == self[2]
34
+ end
35
+ return 2 if self[2] == 0
36
+ return 2 if self[2] == 1 and self[3] == 2
37
+ return 2 if self[2] == 2 and self[3] == 1
38
+ if self[2] > 2
39
+ return 2 if self[2] == self[3]
40
+ end
41
+ end
42
+
43
+ end
44
+ end
45
+
46
+ def fold(line)
47
+ if can_fold?(line) then
48
+ if(line == [0,0,0,0]) then return line end
49
+ return line.squash
50
+ else
51
+ return line
52
+ end
53
+ end
54
+
55
+ def can_fold?(line)
56
+ foldable = has_adjacent_one_and_two?(line)
57
+ foldable = (foldable or has_adjacent_equals?(line))
58
+ foldable = (foldable or has_non_trailing_zeros?(line))
59
+ return foldable
60
+ end
61
+
62
+ def has_non_trailing_zeros?(line)
63
+ first_non_zero_from_end = index_of_first_non_trailing_zero(line)
64
+ if (first_non_zero_from_end == nil) then return true end
65
+ if (line.index(0) == nil) then return false end
66
+ return (line.index(0) < first_non_zero_from_end)
67
+ end
68
+
69
+ def index_of_first_non_trailing_zero(line)
70
+ first_non_zero_from_end = line.rindex {|element| element != 0}
71
+ end
72
+
73
+ def has_adjacent_one_and_two?(line)
74
+ pairs = line.each_cons(2).to_a
75
+ index = pairs.each_index {|i|
76
+ if (pairs[i].sort == [1, 2])
77
+ return true
78
+ end
79
+ }
80
+ false
81
+ end
82
+
83
+ def has_adjacent_equals?(line)
84
+ pairs = line.each_cons(2).to_a
85
+ index = pairs.each {|pair|
86
+ if(pair[0] == pair[1] and (pair[0] > 2))
87
+ return true
88
+ end
89
+ }
90
+ false
91
+ end
92
+
93
+ end
@@ -0,0 +1,21 @@
1
+ class NextNumberDeterminator
2
+ def initialize
3
+ @score_table = [0,1,2,3,6,12,24,48,96,192,384,768,1536,3072,6144]
4
+ end
5
+
6
+ def highest_number(game_board)
7
+ highest = 0
8
+ game_board.row_vectors.each {|row|
9
+ row.to_a.each {|value|
10
+ if (value > highest) then
11
+ highest = value
12
+ end
13
+ }
14
+ }
15
+ return highest
16
+ end
17
+
18
+ def select_number(game_board)
19
+ @score_table[rand(1..([3, @score_table.index(highest_number(game_board))-2].max))]
20
+ end
21
+ end
@@ -0,0 +1,10 @@
1
+ # This class is responsible for selecting the number to add to a certain game
2
+ # board state and to select the cell where the number should go
3
+ class NextNumberPositioner
4
+
5
+ # Selects one of the candidate positions to add the next number
6
+ def select_position(candidates)
7
+ return candidates[rand(0..candidates.size - 1)]
8
+ end
9
+
10
+ end
@@ -0,0 +1,27 @@
1
+ # This class is responsible for calculating the score for a given game board.
2
+ class ScoreCalculator
3
+
4
+ @@score_table = {0 => 0, 1 => 0, 2 => 0,
5
+ 3 => 3, 6 => 9,
6
+ 12 => 27,
7
+ 24 => 81,
8
+ 48 => 243,
9
+ 96 => 729,
10
+ 192 => 2187,
11
+ 384 => 6561,
12
+ 768 => 19683,
13
+ 1536 => 59049,
14
+ 3072 => 177147,
15
+ 6144 => 531441}
16
+
17
+ def self.score_for(game_board)
18
+ score = 0
19
+ game_board.row_vectors.each {|vector|
20
+ vector.to_a.each {|val|
21
+ score = score + @@score_table[val]
22
+ }
23
+ }
24
+ score
25
+ end
26
+
27
+ end
@@ -0,0 +1,3 @@
1
+ module Threesmodel
2
+ VERSION = "0.0.3"
3
+ end
@@ -0,0 +1,113 @@
1
+ require "threesmodel/version"
2
+ require 'candidate_translator'
3
+ require 'candidate_extractor'
4
+ require 'game_board_folder'
5
+ require 'next_number_positioner'
6
+ require 'next_number_determinator'
7
+ require 'game_over_checker'
8
+ require 'game_creator'
9
+ require 'securerandom'
10
+ require 'matrix'
11
+ require 'score_calculator'
12
+
13
+ module Threesmodel
14
+ class InternalAPI
15
+ def initialize
16
+
17
+ end
18
+ end
19
+ class GameController
20
+
21
+ def initialize
22
+ @games = {}
23
+ @next_number = {}
24
+ @candidate_extractor = CandidateExtractor.new
25
+ @candidate_translator = CandidateTranslator.new
26
+ @game_over_checker = GameOverChecker.new
27
+ @next_number_determinator = NextNumberDeterminator.new
28
+ @number_positioner = NextNumberPositioner.new
29
+ @game_board_folder = GameBoardFolder.new
30
+ end
31
+
32
+ def start_new_game
33
+ id = SecureRandom.uuid
34
+ @games[id] = GameCreator.create_new_game
35
+ @next_number[id] = getNextNumber(@games[id])
36
+ respond_to_player(id, @next_number[id])
37
+ end
38
+
39
+ def fold_right(id)
40
+ game = @games[id]
41
+ unless (@game_board_folder.can_fold_right?(game))
42
+ return did_not_fold(game, id)
43
+ end
44
+ candidates = @candidate_translator.translate_right_fold(@candidate_extractor.fold_right_candidates(game))
45
+ game = @game_board_folder.fold_right(game)
46
+ prepare_for_next_fold(candidates, game, id)
47
+ end
48
+
49
+ def fold_left(id)
50
+ game = @games[id]
51
+ unless (@game_board_folder.can_fold_left?(game))
52
+ return did_not_fold(game, id)
53
+ end
54
+ candidates = @candidate_translator.translate_left_fold(@candidate_extractor.fold_left_candidates(game))
55
+ game = @game_board_folder.fold_left(game)
56
+ prepare_for_next_fold(candidates, game, id)
57
+ end
58
+
59
+ def fold_down(id)
60
+ game = @games[id]
61
+ unless (@game_board_folder.can_fold_down?(game))
62
+ return did_not_fold(game, id)
63
+ end
64
+ candidates = @candidate_translator.translate_down_fold(@candidate_extractor.fold_down_candidates(game))
65
+ game = @game_board_folder.fold_down(game)
66
+ prepare_for_next_fold(candidates, game, id)
67
+ end
68
+
69
+ def fold_up(id)
70
+ game = @games[id]
71
+ unless (@game_board_folder.can_fold_up?(game))
72
+ return did_not_fold(game, id)
73
+ end
74
+ candidates = @candidate_translator.translate_up_fold(@candidate_extractor.fold_up_candidates(game))
75
+ game = @game_board_folder.fold_up(game)
76
+ prepare_for_next_fold(candidates, game, id)
77
+ end
78
+
79
+ def did_not_fold(game, id)
80
+ respond_to_player(id, @next_number[id])
81
+ end
82
+
83
+ def prepare_for_next_fold(candidates, game, id)
84
+ game = add_next_number(id, candidates, game)
85
+ @next_number[id] = getNextNumber(game)
86
+ @games[id] = game
87
+ respond_to_player(id, @next_number[id])
88
+ end
89
+
90
+ def add_next_number(id, candidates, game)
91
+ coordinates = @number_positioner.select_position(candidates)
92
+ new_state = []
93
+ game.row_vectors.each_index {|i|
94
+ values = game.row_vectors[i].to_a
95
+ if (coordinates[0] == i)
96
+ values[coordinates[1]] = @next_number[id]
97
+ end
98
+ new_state << values
99
+ }
100
+ Matrix.rows(new_state)
101
+ end
102
+
103
+ def getNextNumber(game)
104
+ @next_number_determinator.select_number(game)
105
+ end
106
+
107
+ def respond_to_player(id, next_number)
108
+ {:id => id, :game => @games[id], :next_number => next_number, :game_over => @game_over_checker.game_over?(@games[id]), :score => ScoreCalculator.score_for(@games[id]) }
109
+ end
110
+
111
+ end
112
+
113
+ end
@@ -0,0 +1,34 @@
1
+ require_relative "../lib/candidate_extractor"
2
+ require "Matrix"
3
+
4
+ describe CandidateExtractor do
5
+
6
+ before(:each) do
7
+ @candidate_extractor = CandidateExtractor.new
8
+ end
9
+
10
+ it "extracts no candidates form an unfoldable board" do
11
+ game = Matrix.rows([[3, 1, 1, 3],
12
+ [6, 1, 1, 6],
13
+ [12, 1, 3, 12],
14
+ [24, 1, 6, 24]])
15
+ expect(@candidate_extractor.fold_left_candidates(game)).to eq([])
16
+ expect(@candidate_extractor.fold_right_candidates(game)).to eq([])
17
+ expect(@candidate_extractor.fold_up_candidates(game)).to eq([])
18
+ expect(@candidate_extractor.fold_down_candidates(game)).to eq([])
19
+ end
20
+
21
+ it "extracts a single candidate form a board with one foldable line" do
22
+ expect(@candidate_extractor.fold_left_candidates(
23
+ Matrix.rows([[3,1,1,3],[6,1,1,6],[12,1,2,12],[24,1,6,24]]))).to eq([[2,3]])
24
+ end
25
+
26
+ it "extracts all four candidates form a board where all lines fold" do
27
+ expect(@candidate_extractor.fold_left_candidates(
28
+ Matrix.rows([[3,3,1,3],
29
+ [6,3,3,6],
30
+ [12,1,12,12],
31
+ [3,3,3,3]]))).to eq([[0,3], [1,3], [2,3], [3,3]])
32
+ end
33
+
34
+ end
@@ -0,0 +1,36 @@
1
+ require_relative '../lib/candidate_translator'
2
+ describe CandidateTranslator do
3
+
4
+ it "translates left fold candidates with same candidates" do
5
+ translator = CandidateTranslator.new
6
+ expect(translator.translate_left_fold([[0,3]])).to eq [[0,3]]
7
+ expect(translator.translate_left_fold([[0,3], [1,3]])).to eq [[0,3], [1,3]]
8
+ expect(translator.translate_left_fold([[0,3], [1,3], [2,3]])).to eq [[0,3], [1,3], [2,3]]
9
+ expect(translator.translate_left_fold([[0,3], [1,3], [2,3], [3,3]])).to eq [[0,3], [1,3], [2,3], [3,3]]
10
+ end
11
+
12
+ it "translates right fold candidates with mirrored candidates" do
13
+ translator = CandidateTranslator.new
14
+ expect(translator.translate_right_fold([[0,3]])).to eq [[3,0]]
15
+ expect(translator.translate_right_fold([[1,3]])).to eq [[2,0]]
16
+ expect(translator.translate_right_fold([[2,3]])).to eq [[1,0]]
17
+ expect(translator.translate_right_fold([[3,3]])).to eq [[0,0]]
18
+ end
19
+
20
+ it "translates down fold candidates" do
21
+ translator = CandidateTranslator.new
22
+ expect(translator.translate_down_fold([[0,3]])).to eq [[0,0]]
23
+ expect(translator.translate_down_fold([[1,3]])).to eq [[0,1]]
24
+ expect(translator.translate_down_fold([[2,3]])).to eq [[0,2]]
25
+ expect(translator.translate_down_fold([[3,3]])).to eq [[0,3]]
26
+ end
27
+
28
+ it "translates up fold candidates" do
29
+ translator = CandidateTranslator.new
30
+ expect(translator.translate_up_fold([[0,3]])).to eq [[3,3]]
31
+ expect(translator.translate_up_fold([[1,3]])).to eq [[3,2]]
32
+ expect(translator.translate_up_fold([[2,3]])).to eq [[3,1]]
33
+ expect(translator.translate_up_fold([[3,3]])).to eq [[3,0]]
34
+ end
35
+
36
+ end
@@ -0,0 +1,27 @@
1
+ module CustomMatchers
2
+ class BeOneOf
3
+ def initialize(expected)
4
+ @expected = expected
5
+ end
6
+
7
+ def matches?(target)
8
+ @target = target
9
+ if(@target.respond_to? :keys) then
10
+ @expected.include?(@target.keys[0])
11
+ else
12
+ @expected.include?(@target)
13
+ end
14
+ end
15
+
16
+ def failure_message
17
+ "expected #{@target} to be one of the elements of #{@expected}"
18
+ end
19
+
20
+ def failure_message_when_negated
21
+ "expected #{@target} not to be one of the elements of #{@expected}"
22
+ end
23
+ end
24
+ def be_one_of(expected)
25
+ BeOneOf.new(expected)
26
+ end
27
+ end
@@ -0,0 +1,50 @@
1
+ require_relative "../lib/game_board_folder"
2
+ require "Matrix"
3
+
4
+ describe GameBoardFolder do
5
+
6
+ before(:each) do
7
+ @game_board_folder = GameBoardFolder.new
8
+ end
9
+
10
+ it "can create game board" do
11
+ expect(@game_board_folder).to_not eq(nil)
12
+ end
13
+
14
+ it "can fold all rows to the left" do
15
+ expect(@game_board_folder.fold_left(
16
+ Matrix.rows([[0,1,2,0],
17
+ [0,1,2,0],
18
+ [0,1,2,0],
19
+ [0,1,2,0]])).row(0).to_a).to eq([1,2,0,0])
20
+ end
21
+
22
+ it "can fold all rows to the right" do
23
+ expect(@game_board_folder.fold_right(
24
+ Matrix.rows([[0,1,2,0],
25
+ [0,1,2,0],
26
+ [0,1,2,0],
27
+ [0,1,2,0]])).row(0).to_a).to eq([0,0,1,2])
28
+ end
29
+
30
+ it "can fold all rows up" do
31
+ state = @game_board_folder.fold_up(
32
+ Matrix.rows([[0,0,0,0],
33
+ [1,1,1,1],
34
+ [2,2,2,2],
35
+ [0,0,0,0]]))
36
+ expect(state.row(0).to_a).to eq([1,1,1,1])
37
+ expect(state.row(1).to_a).to eq([2,2,2,2])
38
+ end
39
+
40
+ it "can fold all rows down" do
41
+ state = @game_board_folder.fold_down(
42
+ Matrix.rows([[0,0,0,0],
43
+ [1,1,1,1],
44
+ [2,2,2,2],
45
+ [0,0,0,0]]))
46
+ expect(state.row(2).to_a).to eq([1,1,1,1])
47
+ expect(state.row(3).to_a).to eq([2,2,2,2])
48
+ end
49
+
50
+ end
@@ -0,0 +1,36 @@
1
+ require 'rspec'
2
+ require_relative '../lib/game_creator'
3
+ require 'custom_matchers'
4
+
5
+ describe 'Create new Game' do
6
+
7
+ include CustomMatchers
8
+
9
+ it 'should randomly position nine cells' do
10
+ game_state = GameCreator.create_new_game()
11
+ expect(cells_in_game(game_state)).to eq(9)
12
+ end
13
+
14
+ it 'should determine nine coordinates' do
15
+ expect(GameCreator.random_positions(9).size).to eq(9)
16
+ end
17
+
18
+ it 'should only use 1s 2s and 3s when initiating game board' do
19
+ game_state = GameCreator.create_new_game()
20
+ game_state.row_vectors.each{|row|
21
+ row.to_a.each{|value|
22
+ expect(value).to be_one_of([0,1,2,3])
23
+ }
24
+ }
25
+ end
26
+ end
27
+
28
+ def cells_in_game(game_state)
29
+ cells = 0
30
+ game_state.row_vectors.each{|row|
31
+ row.to_a.each{|val|
32
+ if val > 0 then cells = cells + 1 end
33
+ }
34
+ }
35
+ cells
36
+ end
@@ -0,0 +1,87 @@
1
+ require_relative "../lib/line_folder"
2
+
3
+ describe LineFolder do
4
+
5
+ before(:each) do
6
+ @line_folder = LineFolder.new
7
+ end
8
+
9
+ it "can find wall in position zero" do
10
+ expect([0,0,1,2].wall_index_of).to eq(0)
11
+ expect([1,2,0,0].wall_index_of).to eq(0)
12
+ expect([2,1,0,0].wall_index_of).to eq(0)
13
+ expect([0,1,0,0].wall_index_of).to eq(0)
14
+ expect([3,3,0,0].wall_index_of).to eq(0)
15
+ end
16
+
17
+ it "can find wall in position one" do
18
+ expect([1,1,2,2].wall_index_of).to eq(1)
19
+ expect([2,2,1,2].wall_index_of).to eq(1)
20
+ expect([3,2,1,2].wall_index_of).to eq(1)
21
+ expect([2,3,3,2].wall_index_of).to eq(1)
22
+ expect([1,0,2,2].wall_index_of).to eq(1)
23
+ end
24
+
25
+ it "can find wall in position two" do
26
+ expect([1,1,1,2].wall_index_of).to eq(2)
27
+ expect([2,2,2,1].wall_index_of).to eq(2)
28
+ expect([2,2,3,3].wall_index_of).to eq(2)
29
+ expect([2,2,0,3].wall_index_of).to eq(2)
30
+ end
31
+
32
+ it "can fold when line has leading zeros" do
33
+ expect(@line_folder.can_fold?([0,0,1,2])).to eq(true)
34
+ end
35
+
36
+ it "can not fold when line is all ones" do
37
+ expect(@line_folder.can_fold?([1,1,1,1])).to eq(false)
38
+ end
39
+
40
+ it "can fold when line has a one and a two adjacent" do
41
+ expect(@line_folder.can_fold?([1,2,1,1])).to eq(true)
42
+ end
43
+
44
+ it "can fold when line has a two and a one adjacent" do
45
+ expect(@line_folder.can_fold?([2,1,1,1])).to eq(true)
46
+ end
47
+
48
+ it "can fold over two equal numbers larger than 2" do
49
+ expect(@line_folder.can_fold?([3,3,6,12])).to eq(true)
50
+ end
51
+
52
+ it "folds undetectably when all zero line" do
53
+ expect(@line_folder.fold([0,0,0,0])).to eq([0,0,0,0])
54
+ end
55
+ it "folds by shifting onto leading zeros" do
56
+ expect(@line_folder.fold([0,0,1,2])).to eq([0,1,2,0])
57
+ end
58
+
59
+ it "folds by shifting onto zeros" do
60
+ expect(@line_folder.fold([1,0,1,2])).to eq([1,1,2,0])
61
+ end
62
+
63
+ it "folds by adding one and two at a wall" do
64
+ expect(@line_folder.fold([1,2,0,0])).to eq([3,0,0,0])
65
+ end
66
+
67
+ it "folds by adding one and two at a non foldable cell" do
68
+ expect(@line_folder.fold([3,2,1,0])).to eq([3,3,0,0])
69
+ end
70
+
71
+ it "folds by adding equal numbers at a wall" do
72
+ expect(@line_folder.fold([3,3,0,0])).to eq([6,0,0,0])
73
+ end
74
+
75
+ it "folds by adding equal numbers at a non foldable cell" do
76
+ expect(@line_folder.fold([12,6,6,3])).to eq([12,12,3,0])
77
+ end
78
+
79
+ it "only folds first pair of equal numbers " do
80
+ expect(@line_folder.fold([12,12,3,3])).to eq([24,3,3,0])
81
+ end
82
+
83
+ it "prioritizes leftwards folds" do
84
+ expect(@line_folder.fold([1,2,0,3])).to eq([3,0,3,0])
85
+ end
86
+
87
+ end