swttt-gem 0.9.0 → 1.0.0
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.
- data/VERSION +1 -1
- data/lib/.DS_Store +0 -0
- data/lib/board.rb +20 -18
- data/lib/game_observer.rb +3 -3
- data/lib/minimax_computer.rb +20 -32
- data/spec/board_spec.rb +31 -11
- data/spec/game_observer_spec.rb +2 -2
- data/swttt-gem.gemspec +3 -2
- metadata +5 -4
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
1.0.0
|
data/lib/.DS_Store
ADDED
Binary file
|
data/lib/board.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
class Board
|
2
|
-
attr_reader :game_history, :
|
2
|
+
attr_reader :game_history, :player_value, :row_scores, :dimension,
|
3
3
|
:column_scores, :left_diagonal_score, :right_diagonal_score
|
4
4
|
|
5
5
|
def initialize(dimension = 3)
|
@@ -25,6 +25,10 @@ class Board
|
|
25
25
|
@game_history.size
|
26
26
|
end
|
27
27
|
|
28
|
+
def value_at(row, column)
|
29
|
+
@board[row][column]
|
30
|
+
end
|
31
|
+
|
28
32
|
def is_empty_at?(row, column)
|
29
33
|
@board[row][column].zero?
|
30
34
|
end
|
@@ -33,8 +37,8 @@ class Board
|
|
33
37
|
@game_history.size == @dimension*@dimension
|
34
38
|
end
|
35
39
|
|
36
|
-
def
|
37
|
-
@dimension
|
40
|
+
def winning_value
|
41
|
+
@dimension
|
38
42
|
end
|
39
43
|
|
40
44
|
def player_value
|
@@ -43,31 +47,29 @@ class Board
|
|
43
47
|
return value
|
44
48
|
end
|
45
49
|
|
46
|
-
def
|
47
|
-
|
48
|
-
|
50
|
+
def corner_occupied?
|
51
|
+
corner_cells.each { |corner| return true if @game_history.include?(corner) }
|
52
|
+
return false
|
49
53
|
end
|
50
54
|
|
51
|
-
|
52
|
-
|
53
|
-
def print_row(row)
|
54
|
-
puts "\n"
|
55
|
-
(0...@dimension).each { |column| print_square(row, column) }
|
55
|
+
def random_corner_cell
|
56
|
+
corner_cells[rand(corner_cells.size)]
|
56
57
|
end
|
57
58
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
59
|
+
private
|
60
|
+
|
61
|
+
def corner_cells
|
62
|
+
[Move.new(0,0),
|
63
|
+
Move.new(0,@dimension-1),
|
64
|
+
Move.new(@dimension-1,0),
|
65
|
+
Move.new(@dimension-1, @dimension-1)]
|
64
66
|
end
|
65
67
|
|
66
68
|
def update_sums(row, column, update_value)
|
67
69
|
@row_scores[row] += update_value
|
68
70
|
@column_scores[column] += update_value
|
69
71
|
@left_diagonal_score += update_value if row == column
|
70
|
-
@right_diagonal_score += update_value if row == dimension - column
|
72
|
+
@right_diagonal_score += update_value if row == dimension - column - 1
|
71
73
|
end
|
72
74
|
end
|
73
75
|
|
data/lib/game_observer.rb
CHANGED
@@ -28,11 +28,11 @@ private
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def check_for_winner
|
31
|
-
(0
|
32
|
-
false
|
31
|
+
(0...@game_board.dimension).each { |index| return true if is_a_win?(yield(index)) }
|
32
|
+
return false
|
33
33
|
end
|
34
34
|
|
35
35
|
def is_a_win?(value)
|
36
|
-
value == @game_board.
|
36
|
+
value == @game_board.winning_value
|
37
37
|
end
|
38
38
|
end
|
data/lib/minimax_computer.rb
CHANGED
@@ -2,10 +2,6 @@ class MinimaxComputer
|
|
2
2
|
|
3
3
|
def initialize(board, observer)
|
4
4
|
@game_board, @observer = board, observer
|
5
|
-
@first_move = [Move.new(0,0),
|
6
|
-
Move.new(0,@game_board.dimension),
|
7
|
-
Move.new(@game_board.dimension,0),
|
8
|
-
Move.new(@game_board.dimension, @game_board.dimension)]
|
9
5
|
@my_player_value = @game_board.player_value
|
10
6
|
end
|
11
7
|
|
@@ -28,37 +24,29 @@ private
|
|
28
24
|
@game_board.move(move.row, move.column)
|
29
25
|
end
|
30
26
|
|
31
|
-
def select_first_move
|
32
|
-
return corner_move if all_board_corners_open?
|
33
|
-
return middle_move
|
34
|
-
end
|
35
|
-
|
36
|
-
def all_board_corners_open?
|
37
|
-
!@first_move.include?(@game_board.game_history.first)
|
38
|
-
end
|
39
|
-
|
40
|
-
def corner_move
|
41
|
-
@first_move[rand(@first_move.size)]
|
42
|
-
end
|
43
|
-
|
44
|
-
def middle_move
|
45
|
-
middle = @game_board.dimension/2
|
46
|
-
return move = Move.new(middle, middle)
|
47
|
-
end
|
48
|
-
|
49
27
|
def perform_mini_max(iteration)
|
50
28
|
return path_score if @observer.game_over?
|
51
29
|
best_moves = BestMove.new(@game_board, @my_player_value)
|
52
|
-
for_each_cell { |row, column| best_moves.add_better_move(
|
30
|
+
for_each_cell { |row, column| best_moves.add_better_move(calculate_path_score(row, column,iteration),
|
53
31
|
Move.new(row, column)) if @game_board.is_empty_at?(row, column) }
|
54
32
|
return best_moves.value if !iteration.zero?
|
55
33
|
move = best_moves.get_random_move
|
56
34
|
@game_board.move(move.row, move.column)
|
57
35
|
end
|
58
36
|
|
59
|
-
|
60
|
-
|
61
|
-
|
37
|
+
def select_first_move
|
38
|
+
return @game_board.random_corner_cell if !@game_board.corner_occupied?
|
39
|
+
return middle_move
|
40
|
+
end
|
41
|
+
|
42
|
+
def middle_move
|
43
|
+
middle = @game_board.dimension/2
|
44
|
+
return move = Move.new(middle, middle)
|
45
|
+
end
|
46
|
+
|
47
|
+
def for_each_cell
|
48
|
+
(0...@game_board.dimension).each do |row|
|
49
|
+
(0...@game_board.dimension).each { |column| yield(row, column) }
|
62
50
|
end
|
63
51
|
end
|
64
52
|
|
@@ -103,16 +91,16 @@ class BestMove
|
|
103
91
|
@moves[rand(@moves.size)]
|
104
92
|
end
|
105
93
|
|
106
|
-
def add_better_move(
|
107
|
-
if better_or_equal_move?(
|
94
|
+
def add_better_move(path_score, move)
|
95
|
+
if better_or_equal_move?(path_score)
|
108
96
|
@moves.clear and @value = path_score if path_score != @value
|
109
97
|
@moves << move
|
110
98
|
end
|
111
99
|
end
|
112
100
|
|
113
|
-
def better_or_equal_move?(
|
114
|
-
(
|
115
|
-
(path_score >=
|
116
|
-
(path_score <=
|
101
|
+
def better_or_equal_move?(path_score)
|
102
|
+
(@value.nil?) or
|
103
|
+
(path_score >= @value && @game_board.player_value == @my_player_value) or
|
104
|
+
(path_score <= @value && @game_board.player_value != @my_player_value)
|
117
105
|
end
|
118
106
|
end
|
data/spec/board_spec.rb
CHANGED
@@ -7,20 +7,20 @@ describe Board do
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def iterate_board
|
10
|
-
(0
|
11
|
-
(0
|
10
|
+
(0...@my_board.dimension).each do |row|
|
11
|
+
(0...@my_board.dimension).each do |column|
|
12
12
|
yield(row, column)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
17
|
it "should create a board" do
|
18
|
-
@my_board.nil?.should
|
18
|
+
@my_board.nil?.should be_false
|
19
19
|
end
|
20
20
|
|
21
21
|
it "should check for a full board" do
|
22
22
|
iterate_board { |row, column| @my_board.move(row, column) }
|
23
|
-
@my_board.full?.should
|
23
|
+
@my_board.full?.should be_true
|
24
24
|
end
|
25
25
|
|
26
26
|
context "game history" do
|
@@ -75,20 +75,15 @@ describe Board do
|
|
75
75
|
@my_board.player_value.should == 1
|
76
76
|
end
|
77
77
|
|
78
|
-
it "has a 3x3 board" do
|
79
|
-
@my_board.board.size.should == 3
|
80
|
-
@my_board.board[1].size.should == 3
|
81
|
-
end
|
82
|
-
|
83
78
|
it "updates the board when move is called" do
|
84
79
|
@my_board.move(0,0)
|
85
|
-
@my_board.
|
80
|
+
@my_board.value_at(0,0).should == 1
|
86
81
|
end
|
87
82
|
|
88
83
|
it "undos the board" do
|
89
84
|
@my_board.move(2,2)
|
90
85
|
@my_board.undo_move
|
91
|
-
@my_board.
|
86
|
+
@my_board.value_at(2,2).should == 0
|
92
87
|
end
|
93
88
|
|
94
89
|
context "sum values" do
|
@@ -126,4 +121,29 @@ describe Board do
|
|
126
121
|
@my_board.right_diagonal_score.should == 0
|
127
122
|
end
|
128
123
|
end
|
124
|
+
|
125
|
+
it "has a winning value" do
|
126
|
+
@my_board.winning_value.should == 3
|
127
|
+
end
|
128
|
+
|
129
|
+
it "detects if a corner cell is occupied" do
|
130
|
+
@my_board.corner_occupied?.should be_false
|
131
|
+
@my_board.move(0,0)
|
132
|
+
@my_board.corner_occupied?.should be_true
|
133
|
+
@my_board.undo_move
|
134
|
+
@my_board.move(0,2)
|
135
|
+
@my_board.corner_occupied?.should be_true
|
136
|
+
@my_board.undo_move
|
137
|
+
@my_board.move(2,0)
|
138
|
+
@my_board.corner_occupied?.should be_true
|
139
|
+
@my_board.undo_move
|
140
|
+
@my_board.move(2,2)
|
141
|
+
@my_board.corner_occupied?.should be_true
|
142
|
+
end
|
143
|
+
|
144
|
+
it "returns a random corner cell" do
|
145
|
+
move = @my_board.random_corner_cell
|
146
|
+
@my_board.move(move.row, move.column)
|
147
|
+
@my_board.corner_occupied?.should be_true
|
148
|
+
end
|
129
149
|
end
|
data/spec/game_observer_spec.rb
CHANGED
@@ -9,7 +9,7 @@ describe GameObserver do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def iterate_row_or_column
|
12
|
-
(0
|
12
|
+
(0...@my_board.dimension).each { |position| yield(position) }
|
13
13
|
end
|
14
14
|
|
15
15
|
it "detects a horizontal win" do
|
@@ -28,7 +28,7 @@ describe GameObserver do
|
|
28
28
|
end
|
29
29
|
|
30
30
|
it "detects a right diagonal win" do
|
31
|
-
iterate_row_or_column { |position| @my_board.move(@my_board.dimension-position,position,1) }
|
31
|
+
iterate_row_or_column { |position| @my_board.move(@my_board.dimension-position-1,position,1) }
|
32
32
|
@observer.has_winner?.should == true
|
33
33
|
end
|
34
34
|
|
data/swttt-gem.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{swttt-gem}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "1.0.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Stephen Walker"]
|
12
|
-
s.date = %q{2011-05-
|
12
|
+
s.date = %q{2011-05-11}
|
13
13
|
s.description = %q{TTT Gem}
|
14
14
|
s.email = %q{stephenwalker1988@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -25,6 +25,7 @@ Gem::Specification.new do |s|
|
|
25
25
|
"README.rdoc",
|
26
26
|
"Rakefile",
|
27
27
|
"VERSION",
|
28
|
+
"lib/.DS_Store",
|
28
29
|
"lib/board.rb",
|
29
30
|
"lib/game_observer.rb",
|
30
31
|
"lib/human_player.rb",
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: swttt-gem
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
|
+
- 1
|
7
8
|
- 0
|
8
|
-
- 9
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 1.0.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Stephen Walker
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-05-
|
18
|
+
date: 2011-05-11 00:00:00 -05:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -96,6 +96,7 @@ files:
|
|
96
96
|
- README.rdoc
|
97
97
|
- Rakefile
|
98
98
|
- VERSION
|
99
|
+
- lib/.DS_Store
|
99
100
|
- lib/board.rb
|
100
101
|
- lib/game_observer.rb
|
101
102
|
- lib/human_player.rb
|