tic_tac_toes 0.0.7 → 0.0.8
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 +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
|