tic_tac_toes 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/Rakefile +2 -2
- data/bin/tic_tac_toes +18 -22
- data/lib/tic_tac_toes/command_line/menu.rb +55 -0
- data/lib/tic_tac_toes/command_line/prompt.rb +29 -0
- data/lib/tic_tac_toes/command_line/runner.rb +38 -0
- data/lib/tic_tac_toes/core/board.rb +64 -0
- data/lib/tic_tac_toes/core/board_factory.rb +11 -0
- data/lib/tic_tac_toes/core/game_state.rb +40 -0
- data/lib/tic_tac_toes/core/game_state_factory.rb +15 -0
- data/lib/tic_tac_toes/core/history.rb +29 -0
- data/lib/tic_tac_toes/core/io.rb +93 -0
- data/lib/tic_tac_toes/core/move_strategies/easy_ai.rb +13 -0
- data/lib/tic_tac_toes/core/move_strategies/hard_ai.rb +89 -0
- data/lib/tic_tac_toes/core/move_strategies/human.rb +20 -0
- data/lib/tic_tac_toes/core/move_strategies/medium_ai.rb +15 -0
- data/lib/tic_tac_toes/core/player.rb +22 -0
- data/lib/tic_tac_toes/core/player_factory.rb +30 -0
- data/lib/tic_tac_toes/core/rules.rb +66 -0
- data/lib/tic_tac_toes/core/strings.rb +106 -0
- data/lib/tic_tac_toes/database/pg_wrapper.rb +74 -0
- data/lib/tic_tac_toes/ui/adapter.rb +96 -0
- data/lib/tic_tac_toes/ui/serializer.rb +0 -0
- data/lib/tic_tac_toes/version.rb +1 -1
- data/spec/{command_line → tic_tac_toes/command_line}/menu_spec.rb +14 -14
- data/spec/{command_line → tic_tac_toes/command_line}/runner_spec.rb +7 -10
- data/spec/tic_tac_toes/{board_factory_spec.rb → core/board_factory_spec.rb} +3 -3
- data/spec/tic_tac_toes/{board_spec.rb → core/board_spec.rb} +23 -23
- data/spec/tic_tac_toes/{game_state_factory_spec.rb → core/game_state_factory_spec.rb} +3 -3
- data/spec/tic_tac_toes/core/game_state_spec.rb +129 -0
- data/spec/tic_tac_toes/{history_spec.rb → core/history_spec.rb} +3 -3
- data/spec/tic_tac_toes/{io_spec.rb → core/io_spec.rb} +7 -7
- data/spec/tic_tac_toes/core/move_strategies/easy_ai_spec.rb +19 -0
- data/spec/tic_tac_toes/core/move_strategies/hard_ai_spec.rb +121 -0
- data/spec/tic_tac_toes/{move_strategies → core/move_strategies}/human_spec.rb +3 -3
- data/spec/tic_tac_toes/core/move_strategies/medium_ai_spec.rb +21 -0
- data/spec/tic_tac_toes/{player_factory_spec.rb → core/player_factory_spec.rb} +7 -7
- data/spec/tic_tac_toes/{player_spec.rb → core/player_spec.rb} +5 -5
- data/spec/tic_tac_toes/{rules_spec.rb → core/rules_spec.rb} +34 -34
- data/spec/tic_tac_toes/{strings_spec.rb → core/strings_spec.rb} +8 -8
- data/spec/{database → tic_tac_toes/database}/pg_wrapper_spec.rb +3 -3
- data/spec/tic_tac_toes/test_board_generator.rb +17 -0
- data/spec/{ui → tic_tac_toes/ui}/adapter_spec.rb +13 -13
- data/spec/tic_tac_toes/ui/serializer_spec.rb +0 -0
- metadata +64 -61
- data/lib/command_line/menu.rb +0 -53
- data/lib/command_line/prompt.rb +0 -27
- data/lib/command_line/runner.rb +0 -37
- data/lib/database/pg_wrapper.rb +0 -72
- data/lib/tic_tac_toes/board.rb +0 -62
- data/lib/tic_tac_toes/board_factory.rb +0 -9
- data/lib/tic_tac_toes/game_state.rb +0 -27
- data/lib/tic_tac_toes/game_state_factory.rb +0 -14
- data/lib/tic_tac_toes/history.rb +0 -27
- data/lib/tic_tac_toes/io.rb +0 -91
- data/lib/tic_tac_toes/move_strategies/easy_ai.rb +0 -11
- data/lib/tic_tac_toes/move_strategies/hard_ai.rb +0 -87
- data/lib/tic_tac_toes/move_strategies/human.rb +0 -18
- data/lib/tic_tac_toes/move_strategies/medium_ai.rb +0 -13
- data/lib/tic_tac_toes/player.rb +0 -20
- data/lib/tic_tac_toes/player_factory.rb +0 -28
- data/lib/tic_tac_toes/rules.rb +0 -64
- data/lib/tic_tac_toes/strings.rb +0 -104
- data/lib/ui/adapter.rb +0 -94
- data/spec/test_board_generator.rb +0 -15
- data/spec/tic_tac_toes/game_state_spec.rb +0 -66
- data/spec/tic_tac_toes/move_strategies/easy_ai_spec.rb +0 -19
- data/spec/tic_tac_toes/move_strategies/hard_ai_spec.rb +0 -121
- data/spec/tic_tac_toes/move_strategies/medium_ai_spec.rb +0 -21
- /data/lib/{tasks → tic_tac_toes/tasks}/destroy_databases.rake +0 -0
- /data/lib/{tasks → tic_tac_toes/tasks}/set_up_databases.rake +0 -0
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tic_tac_toes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Spatafora
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-07-
|
11
|
+
date: 2014-07-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -86,47 +86,49 @@ files:
|
|
86
86
|
- bin/destroy_ttt_databases
|
87
87
|
- bin/set_up_ttt_databases
|
88
88
|
- bin/tic_tac_toes
|
89
|
-
- lib/command_line/menu.rb
|
90
|
-
- lib/command_line/prompt.rb
|
91
|
-
- lib/command_line/runner.rb
|
92
|
-
- lib/
|
93
|
-
- lib/
|
94
|
-
- lib/
|
95
|
-
- lib/tic_tac_toes/
|
96
|
-
- lib/tic_tac_toes/
|
97
|
-
- lib/tic_tac_toes/
|
98
|
-
- lib/tic_tac_toes/
|
99
|
-
- lib/tic_tac_toes/
|
100
|
-
- lib/tic_tac_toes/
|
101
|
-
- lib/tic_tac_toes/move_strategies/
|
102
|
-
- lib/tic_tac_toes/
|
103
|
-
- lib/tic_tac_toes/
|
104
|
-
- lib/tic_tac_toes/
|
105
|
-
- lib/tic_tac_toes/
|
106
|
-
- lib/tic_tac_toes/
|
107
|
-
- lib/tic_tac_toes/
|
108
|
-
- lib/tic_tac_toes/
|
89
|
+
- lib/tic_tac_toes/command_line/menu.rb
|
90
|
+
- lib/tic_tac_toes/command_line/prompt.rb
|
91
|
+
- lib/tic_tac_toes/command_line/runner.rb
|
92
|
+
- lib/tic_tac_toes/core/board.rb
|
93
|
+
- lib/tic_tac_toes/core/board_factory.rb
|
94
|
+
- lib/tic_tac_toes/core/game_state.rb
|
95
|
+
- lib/tic_tac_toes/core/game_state_factory.rb
|
96
|
+
- lib/tic_tac_toes/core/history.rb
|
97
|
+
- lib/tic_tac_toes/core/io.rb
|
98
|
+
- lib/tic_tac_toes/core/move_strategies/easy_ai.rb
|
99
|
+
- lib/tic_tac_toes/core/move_strategies/hard_ai.rb
|
100
|
+
- lib/tic_tac_toes/core/move_strategies/human.rb
|
101
|
+
- lib/tic_tac_toes/core/move_strategies/medium_ai.rb
|
102
|
+
- lib/tic_tac_toes/core/player.rb
|
103
|
+
- lib/tic_tac_toes/core/player_factory.rb
|
104
|
+
- lib/tic_tac_toes/core/rules.rb
|
105
|
+
- lib/tic_tac_toes/core/strings.rb
|
106
|
+
- lib/tic_tac_toes/database/pg_wrapper.rb
|
107
|
+
- lib/tic_tac_toes/tasks/destroy_databases.rake
|
108
|
+
- lib/tic_tac_toes/tasks/set_up_databases.rake
|
109
|
+
- lib/tic_tac_toes/ui/adapter.rb
|
110
|
+
- lib/tic_tac_toes/ui/serializer.rb
|
109
111
|
- lib/tic_tac_toes/version.rb
|
110
|
-
-
|
111
|
-
- spec/command_line/
|
112
|
-
- spec/
|
113
|
-
- spec/
|
114
|
-
- spec/
|
115
|
-
- spec/tic_tac_toes/
|
116
|
-
- spec/tic_tac_toes/
|
117
|
-
- spec/tic_tac_toes/
|
118
|
-
- spec/tic_tac_toes/
|
119
|
-
- spec/tic_tac_toes/
|
120
|
-
- spec/tic_tac_toes/
|
121
|
-
- spec/tic_tac_toes/move_strategies/
|
122
|
-
- spec/tic_tac_toes/
|
123
|
-
- spec/tic_tac_toes/
|
124
|
-
- spec/tic_tac_toes/
|
125
|
-
- spec/tic_tac_toes/
|
126
|
-
- spec/tic_tac_toes/
|
127
|
-
- spec/tic_tac_toes/
|
128
|
-
- spec/tic_tac_toes/
|
129
|
-
- spec/ui/
|
112
|
+
- spec/tic_tac_toes/command_line/menu_spec.rb
|
113
|
+
- spec/tic_tac_toes/command_line/runner_spec.rb
|
114
|
+
- spec/tic_tac_toes/core/board_factory_spec.rb
|
115
|
+
- spec/tic_tac_toes/core/board_spec.rb
|
116
|
+
- spec/tic_tac_toes/core/game_state_factory_spec.rb
|
117
|
+
- spec/tic_tac_toes/core/game_state_spec.rb
|
118
|
+
- spec/tic_tac_toes/core/history_spec.rb
|
119
|
+
- spec/tic_tac_toes/core/io_spec.rb
|
120
|
+
- spec/tic_tac_toes/core/move_strategies/easy_ai_spec.rb
|
121
|
+
- spec/tic_tac_toes/core/move_strategies/hard_ai_spec.rb
|
122
|
+
- spec/tic_tac_toes/core/move_strategies/human_spec.rb
|
123
|
+
- spec/tic_tac_toes/core/move_strategies/medium_ai_spec.rb
|
124
|
+
- spec/tic_tac_toes/core/player_factory_spec.rb
|
125
|
+
- spec/tic_tac_toes/core/player_spec.rb
|
126
|
+
- spec/tic_tac_toes/core/rules_spec.rb
|
127
|
+
- spec/tic_tac_toes/core/strings_spec.rb
|
128
|
+
- spec/tic_tac_toes/database/pg_wrapper_spec.rb
|
129
|
+
- spec/tic_tac_toes/test_board_generator.rb
|
130
|
+
- spec/tic_tac_toes/ui/adapter_spec.rb
|
131
|
+
- spec/tic_tac_toes/ui/serializer_spec.rb
|
130
132
|
- tic_tac_toes.gemspec
|
131
133
|
homepage: http://github.com/bspatafora/tic_tac_toes
|
132
134
|
licenses:
|
@@ -153,22 +155,23 @@ signing_key:
|
|
153
155
|
specification_version: 4
|
154
156
|
summary: The game Tic-tac-toe, featuring an unbeatable AI
|
155
157
|
test_files:
|
156
|
-
- spec/command_line/menu_spec.rb
|
157
|
-
- spec/command_line/runner_spec.rb
|
158
|
-
- spec/
|
159
|
-
- spec/
|
160
|
-
- spec/tic_tac_toes/
|
161
|
-
- spec/tic_tac_toes/
|
162
|
-
- spec/tic_tac_toes/
|
163
|
-
- spec/tic_tac_toes/
|
164
|
-
- spec/tic_tac_toes/
|
165
|
-
- spec/tic_tac_toes/
|
166
|
-
- spec/tic_tac_toes/move_strategies/
|
167
|
-
- spec/tic_tac_toes/move_strategies/
|
168
|
-
- spec/tic_tac_toes/
|
169
|
-
- spec/tic_tac_toes/
|
170
|
-
- spec/tic_tac_toes/
|
171
|
-
- spec/tic_tac_toes/
|
172
|
-
- spec/tic_tac_toes/
|
173
|
-
- spec/tic_tac_toes/
|
174
|
-
- spec/ui/adapter_spec.rb
|
158
|
+
- spec/tic_tac_toes/command_line/menu_spec.rb
|
159
|
+
- spec/tic_tac_toes/command_line/runner_spec.rb
|
160
|
+
- spec/tic_tac_toes/core/board_factory_spec.rb
|
161
|
+
- spec/tic_tac_toes/core/board_spec.rb
|
162
|
+
- spec/tic_tac_toes/core/game_state_factory_spec.rb
|
163
|
+
- spec/tic_tac_toes/core/game_state_spec.rb
|
164
|
+
- spec/tic_tac_toes/core/history_spec.rb
|
165
|
+
- spec/tic_tac_toes/core/io_spec.rb
|
166
|
+
- spec/tic_tac_toes/core/move_strategies/easy_ai_spec.rb
|
167
|
+
- spec/tic_tac_toes/core/move_strategies/hard_ai_spec.rb
|
168
|
+
- spec/tic_tac_toes/core/move_strategies/human_spec.rb
|
169
|
+
- spec/tic_tac_toes/core/move_strategies/medium_ai_spec.rb
|
170
|
+
- spec/tic_tac_toes/core/player_factory_spec.rb
|
171
|
+
- spec/tic_tac_toes/core/player_spec.rb
|
172
|
+
- spec/tic_tac_toes/core/rules_spec.rb
|
173
|
+
- spec/tic_tac_toes/core/strings_spec.rb
|
174
|
+
- spec/tic_tac_toes/database/pg_wrapper_spec.rb
|
175
|
+
- spec/tic_tac_toes/test_board_generator.rb
|
176
|
+
- spec/tic_tac_toes/ui/adapter_spec.rb
|
177
|
+
- spec/tic_tac_toes/ui/serializer_spec.rb
|
data/lib/command_line/menu.rb
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
require 'tic_tac_toes/rules'
|
2
|
-
|
3
|
-
module CommandLine
|
4
|
-
class Menu
|
5
|
-
def initialize(io, board_factory, player_factory)
|
6
|
-
@io = io
|
7
|
-
@board_factory = board_factory
|
8
|
-
@player_factory = player_factory
|
9
|
-
end
|
10
|
-
|
11
|
-
def get_board
|
12
|
-
@board_factory.generate_board(get_row_size)
|
13
|
-
end
|
14
|
-
|
15
|
-
def get_players
|
16
|
-
taken_tokens = []
|
17
|
-
human_token = get_token(:human, taken_tokens)
|
18
|
-
taken_tokens << human_token
|
19
|
-
computer_token = get_token(:computer, taken_tokens)
|
20
|
-
difficulty = get_difficulty
|
21
|
-
|
22
|
-
human_player = @player_factory.generate_human_player(human_token)
|
23
|
-
computer_player = @player_factory.generate_computer_player(computer_token, difficulty)
|
24
|
-
[human_player, computer_player]
|
25
|
-
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
def get_row_size
|
30
|
-
loop do
|
31
|
-
row_size = @io.get_row_size
|
32
|
-
break row_size if TicTacToes::Rules.row_size_valid?(row_size)
|
33
|
-
@io.invalid_row_size_error
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
def get_token(player, taken_tokens)
|
38
|
-
loop do
|
39
|
-
token = @io.get_token(player)
|
40
|
-
break token if TicTacToes::Rules.token_valid?(token, taken_tokens)
|
41
|
-
@io.invalid_token_error
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def get_difficulty
|
46
|
-
loop do
|
47
|
-
difficulty = @io.get_difficulty
|
48
|
-
break difficulty if TicTacToes::Rules.difficulty_valid?(difficulty)
|
49
|
-
@io.invalid_difficulty_error
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
data/lib/command_line/prompt.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
module CommandLine
|
2
|
-
module Prompt
|
3
|
-
def self.solicit_input
|
4
|
-
gets.chomp
|
5
|
-
end
|
6
|
-
|
7
|
-
def self.display(message)
|
8
|
-
puts message
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.display_red(message)
|
12
|
-
puts red(message)
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.red(message)
|
16
|
-
colorize(message, 31)
|
17
|
-
end
|
18
|
-
|
19
|
-
def self.blue(message)
|
20
|
-
colorize(message, 34)
|
21
|
-
end
|
22
|
-
|
23
|
-
def self.colorize(message, color_code)
|
24
|
-
"\e[#{color_code}m#{message}\e[0m"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
data/lib/command_line/runner.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
module CommandLine
|
2
|
-
class Runner
|
3
|
-
def initialize(io, menu, game_state_factory, rules)
|
4
|
-
@io = io
|
5
|
-
@menu = menu
|
6
|
-
@game_state_factory = game_state_factory
|
7
|
-
@rules = rules
|
8
|
-
end
|
9
|
-
|
10
|
-
def run
|
11
|
-
board, players = @menu.get_board, @menu.get_players
|
12
|
-
game_state = @game_state_factory.generate_game_state(board, players)
|
13
|
-
|
14
|
-
take_turn(game_state) until @rules.game_over?(game_state.board, game_state.players)
|
15
|
-
end_game(game_state)
|
16
|
-
end
|
17
|
-
|
18
|
-
private
|
19
|
-
|
20
|
-
def take_turn(game_state)
|
21
|
-
@io.draw_board(game_state.board)
|
22
|
-
@io.thinking_notification if game_state.current_player.needs_to_think
|
23
|
-
|
24
|
-
move = game_state.current_player.place_and_return_move(game_state.board, game_state.players)
|
25
|
-
game_state.turn_over(move)
|
26
|
-
end
|
27
|
-
|
28
|
-
def end_game(game_state)
|
29
|
-
@io.draw_board(game_state.board)
|
30
|
-
|
31
|
-
winner = @rules.determine_winner(game_state.board, game_state.players)
|
32
|
-
game_state.game_over(winner)
|
33
|
-
|
34
|
-
@io.game_over_notification(winner)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
data/lib/database/pg_wrapper.rb
DELETED
@@ -1,72 +0,0 @@
|
|
1
|
-
require 'pg'
|
2
|
-
require 'tic_tac_toes/history'
|
3
|
-
|
4
|
-
module Database
|
5
|
-
class PGWrapper
|
6
|
-
def initialize(database)
|
7
|
-
@database = database
|
8
|
-
end
|
9
|
-
|
10
|
-
def record_game_history(history)
|
11
|
-
connection = establish_connection
|
12
|
-
record_board_size_and_winner(history, connection)
|
13
|
-
record_moves(history, connection)
|
14
|
-
end
|
15
|
-
|
16
|
-
def read_game_histories
|
17
|
-
connection = establish_connection
|
18
|
-
games_result = connection.exec("SELECT * FROM games")
|
19
|
-
games = []
|
20
|
-
|
21
|
-
games_result.each_row do |row|
|
22
|
-
game_id = row[0]
|
23
|
-
board_size = row[1].to_i
|
24
|
-
winner = row[2]
|
25
|
-
moves_result = connection.exec("SELECT * FROM moves WHERE game = #{game_id}")
|
26
|
-
|
27
|
-
history = TicTacToes::History.new(self)
|
28
|
-
|
29
|
-
moves_result.each_row do |row|
|
30
|
-
token, space = row[2], row[3].to_i
|
31
|
-
history.record_move([token, space])
|
32
|
-
end
|
33
|
-
history.record_board_size(board_size)
|
34
|
-
history.record_winner(winner)
|
35
|
-
|
36
|
-
games << history
|
37
|
-
end
|
38
|
-
|
39
|
-
games
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def establish_connection
|
45
|
-
PG.connect(dbname: @database)
|
46
|
-
end
|
47
|
-
|
48
|
-
def record_board_size_and_winner(history, connection)
|
49
|
-
connection.exec("INSERT INTO games (board_size, winner) VALUES (
|
50
|
-
#{history.board_size},
|
51
|
-
'#{history.winner}')")
|
52
|
-
end
|
53
|
-
|
54
|
-
def record_moves(history, connection)
|
55
|
-
game_id = read_game_id(connection)
|
56
|
-
|
57
|
-
history.moves.each_with_index do |move, index|
|
58
|
-
move_number = index + 1
|
59
|
-
connection.exec("INSERT INTO moves (game, number, token, space) VALUES (
|
60
|
-
#{game_id},
|
61
|
-
#{move_number},
|
62
|
-
'#{move.first}',
|
63
|
-
#{move.last})")
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def read_game_id(connection)
|
68
|
-
result = connection.exec("SELECT currval(pg_get_serial_sequence('games','id'))")
|
69
|
-
result.getvalue(0,0)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
data/lib/tic_tac_toes/board.rb
DELETED
@@ -1,62 +0,0 @@
|
|
1
|
-
module TicTacToes
|
2
|
-
class Board
|
3
|
-
attr_reader :row_size, :size, :spaces
|
4
|
-
|
5
|
-
def initialize(row_size: 3)
|
6
|
-
@row_size = row_size
|
7
|
-
@size = @row_size**2
|
8
|
-
@spaces = Array.new(@size)
|
9
|
-
end
|
10
|
-
|
11
|
-
def place(player, space)
|
12
|
-
@spaces[space] = player if valid?(space)
|
13
|
-
end
|
14
|
-
|
15
|
-
def space(space)
|
16
|
-
@spaces[space]
|
17
|
-
end
|
18
|
-
|
19
|
-
def open_spaces
|
20
|
-
open_spaces = []
|
21
|
-
|
22
|
-
@spaces.each_with_index do |space, index|
|
23
|
-
open_spaces << index if space.nil?
|
24
|
-
end
|
25
|
-
|
26
|
-
open_spaces
|
27
|
-
end
|
28
|
-
|
29
|
-
def rows
|
30
|
-
@spaces.each_slice(@row_size).to_a
|
31
|
-
end
|
32
|
-
|
33
|
-
def columns
|
34
|
-
rows.transpose
|
35
|
-
end
|
36
|
-
|
37
|
-
def diagonals
|
38
|
-
back_diagonal, front_diagonal = [], []
|
39
|
-
|
40
|
-
rows.each_with_index do |row, index|
|
41
|
-
back_diagonal << row[index]
|
42
|
-
front_diagonal << row[@row_size - (index + 1)]
|
43
|
-
end
|
44
|
-
|
45
|
-
[back_diagonal, front_diagonal]
|
46
|
-
end
|
47
|
-
|
48
|
-
def full?
|
49
|
-
@spaces.all? { |space| !space.nil? }
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
def valid?(space)
|
55
|
-
space_empty = @spaces[space].nil?
|
56
|
-
board_range = 0..(@size - 1)
|
57
|
-
on_the_board = board_range.include? space
|
58
|
-
|
59
|
-
space_empty && on_the_board
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
module TicTacToes
|
2
|
-
class GameState
|
3
|
-
attr_reader :board, :players
|
4
|
-
|
5
|
-
def initialize(board, players, history)
|
6
|
-
@board = board
|
7
|
-
@players = players
|
8
|
-
@history = history
|
9
|
-
|
10
|
-
@history.record_board_size(@board.size)
|
11
|
-
end
|
12
|
-
|
13
|
-
def current_player
|
14
|
-
@players.first
|
15
|
-
end
|
16
|
-
|
17
|
-
def turn_over(move)
|
18
|
-
@history.record_move(move)
|
19
|
-
@players.rotate!
|
20
|
-
end
|
21
|
-
|
22
|
-
def game_over(winner)
|
23
|
-
@history.record_winner(winner)
|
24
|
-
@history.persist
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,14 +0,0 @@
|
|
1
|
-
require 'tic_tac_toes/game_state'
|
2
|
-
require 'tic_tac_toes/history'
|
3
|
-
|
4
|
-
module TicTacToes
|
5
|
-
class GameStateFactory
|
6
|
-
def initialize(history)
|
7
|
-
@history = history
|
8
|
-
end
|
9
|
-
|
10
|
-
def generate_game_state(board, players)
|
11
|
-
TicTacToes::GameState.new(board, players, @history)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
data/lib/tic_tac_toes/history.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
module TicTacToes
|
2
|
-
class History
|
3
|
-
attr_reader :board_size, :moves, :winner
|
4
|
-
|
5
|
-
def initialize(database_wrapper)
|
6
|
-
@database_wrapper = database_wrapper
|
7
|
-
end
|
8
|
-
|
9
|
-
def record_board_size(size)
|
10
|
-
@board_size = size
|
11
|
-
end
|
12
|
-
|
13
|
-
def record_move(move)
|
14
|
-
@moves ||= []
|
15
|
-
@moves << move
|
16
|
-
end
|
17
|
-
|
18
|
-
def record_winner(winner)
|
19
|
-
winner = "Draw" if winner.nil?
|
20
|
-
@winner = winner
|
21
|
-
end
|
22
|
-
|
23
|
-
def persist
|
24
|
-
@database_wrapper.record_game_history(self)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
data/lib/tic_tac_toes/io.rb
DELETED
@@ -1,91 +0,0 @@
|
|
1
|
-
require 'tic_tac_toes/strings'
|
2
|
-
|
3
|
-
module TicTacToes
|
4
|
-
class IO
|
5
|
-
def initialize(io_strategy)
|
6
|
-
@io_strategy = io_strategy
|
7
|
-
end
|
8
|
-
|
9
|
-
def get_row_size
|
10
|
-
row_size_solicitation
|
11
|
-
|
12
|
-
Integer(@io_strategy.solicit_input)
|
13
|
-
rescue ArgumentError
|
14
|
-
not_an_integer_error
|
15
|
-
get_row_size
|
16
|
-
end
|
17
|
-
|
18
|
-
def get_token(player)
|
19
|
-
token_solicitation(player)
|
20
|
-
@io_strategy.solicit_input
|
21
|
-
end
|
22
|
-
|
23
|
-
def get_difficulty
|
24
|
-
difficulty_solicitation
|
25
|
-
@io_strategy.solicit_input.downcase.to_sym
|
26
|
-
end
|
27
|
-
|
28
|
-
def draw_board(board)
|
29
|
-
@io_strategy.display(Strings.board(board))
|
30
|
-
end
|
31
|
-
|
32
|
-
def invalid_row_size_error
|
33
|
-
@io_strategy.display_red(Strings::INVALID_ROW_SIZE)
|
34
|
-
end
|
35
|
-
|
36
|
-
def invalid_token_error
|
37
|
-
@io_strategy.display_red(Strings::INVALID_TOKEN)
|
38
|
-
end
|
39
|
-
|
40
|
-
def invalid_difficulty_error
|
41
|
-
@io_strategy.display_red(Strings::INVALID_DIFFICULTY)
|
42
|
-
end
|
43
|
-
|
44
|
-
def invalid_move_error
|
45
|
-
@io_strategy.display_red(Strings::INVALID_MOVE)
|
46
|
-
end
|
47
|
-
|
48
|
-
def thinking_notification
|
49
|
-
@io_strategy.display_red(Strings::THINKING)
|
50
|
-
end
|
51
|
-
|
52
|
-
def game_over_notification(winner)
|
53
|
-
winner = "Nobody" if winner.nil?
|
54
|
-
@io_strategy.display(Strings.game_over_notification(winner))
|
55
|
-
end
|
56
|
-
|
57
|
-
def move_solicitation
|
58
|
-
@io_strategy.display(Strings::MOVE_SOLICITATION)
|
59
|
-
end
|
60
|
-
|
61
|
-
def not_an_integer_error
|
62
|
-
@io_strategy.display_red(Strings::NOT_AN_INTEGER)
|
63
|
-
end
|
64
|
-
|
65
|
-
def solicit_input
|
66
|
-
@io_strategy.solicit_input
|
67
|
-
end
|
68
|
-
|
69
|
-
def red(message)
|
70
|
-
@io_strategy.red(message)
|
71
|
-
end
|
72
|
-
|
73
|
-
def blue(message)
|
74
|
-
@io_strategy.blue(message)
|
75
|
-
end
|
76
|
-
|
77
|
-
private
|
78
|
-
|
79
|
-
def row_size_solicitation
|
80
|
-
@io_strategy.display(Strings::ROW_SIZE_SOLICITATION)
|
81
|
-
end
|
82
|
-
|
83
|
-
def token_solicitation(player)
|
84
|
-
@io_strategy.display(Strings.token_solicitation(player))
|
85
|
-
end
|
86
|
-
|
87
|
-
def difficulty_solicitation
|
88
|
-
@io_strategy.display(Strings::DIFFICULTY_SOLICITATION)
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
@@ -1,87 +0,0 @@
|
|
1
|
-
require 'tic_tac_toes/rules'
|
2
|
-
|
3
|
-
module TicTacToes
|
4
|
-
module MoveStrategies
|
5
|
-
module HardAI
|
6
|
-
def self.move(board, players)
|
7
|
-
return second_move(board) if nine_board_second_move?(board)
|
8
|
-
|
9
|
-
open_spaces = Hash[board.open_spaces.map { |space| [space, nil] }]
|
10
|
-
|
11
|
-
open_spaces.each do |space, score|
|
12
|
-
score = minimax(generate_board(players.first, space, board), :min, players)
|
13
|
-
open_spaces[space] = score
|
14
|
-
end
|
15
|
-
|
16
|
-
best_score = open_spaces.values.max
|
17
|
-
open_spaces.each { |space, score| return space if score == best_score }
|
18
|
-
end
|
19
|
-
|
20
|
-
private
|
21
|
-
|
22
|
-
def self.minimax(board, current_player, players)
|
23
|
-
return score(board, players) if Rules.game_over?(board, players)
|
24
|
-
|
25
|
-
if current_player == :max
|
26
|
-
best_score = -1
|
27
|
-
board.open_spaces.each do |space|
|
28
|
-
score = minimax(generate_board(players.first, space, board), :min, players)
|
29
|
-
best_score = [best_score, score].max
|
30
|
-
end
|
31
|
-
best_score
|
32
|
-
|
33
|
-
elsif current_player == :min
|
34
|
-
best_score = 1
|
35
|
-
board.open_spaces.each do |space|
|
36
|
-
score = minimax(generate_board(players.last, space, board), :max, players)
|
37
|
-
best_score = [best_score, score].min
|
38
|
-
end
|
39
|
-
best_score
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def self.generate_board(player, space, board)
|
44
|
-
new_board = Marshal.load(Marshal.dump(board))
|
45
|
-
new_board.place(player, space)
|
46
|
-
new_board
|
47
|
-
end
|
48
|
-
|
49
|
-
def self.score(board, players)
|
50
|
-
own_token, opponent_token = players.first.token, players.last.token
|
51
|
-
|
52
|
-
case Rules.determine_winner(board, players)
|
53
|
-
when own_token
|
54
|
-
1
|
55
|
-
when opponent_token
|
56
|
-
-1
|
57
|
-
else
|
58
|
-
0
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
def self.nine_board_second_move?(board)
|
63
|
-
board.size == 9 && board.open_spaces.count == 8
|
64
|
-
end
|
65
|
-
|
66
|
-
def self.second_move(board)
|
67
|
-
if nine_board_corner_occupied(board) || nine_board_side_occupied(board)
|
68
|
-
4
|
69
|
-
elsif nine_board_center_occupied(board)
|
70
|
-
0
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def self.nine_board_corner_occupied(board)
|
75
|
-
board.space(0) || board.space(2) || board.space(6) || board.space(8)
|
76
|
-
end
|
77
|
-
|
78
|
-
def self.nine_board_side_occupied(board)
|
79
|
-
board.space(1) || board.space(3) || board.space(5) || board.space(7)
|
80
|
-
end
|
81
|
-
|
82
|
-
def self.nine_board_center_occupied(board)
|
83
|
-
board.space(4) ? true : false
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|