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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/Rakefile +2 -2
  4. data/bin/tic_tac_toes +18 -22
  5. data/lib/tic_tac_toes/command_line/menu.rb +55 -0
  6. data/lib/tic_tac_toes/command_line/prompt.rb +29 -0
  7. data/lib/tic_tac_toes/command_line/runner.rb +38 -0
  8. data/lib/tic_tac_toes/core/board.rb +64 -0
  9. data/lib/tic_tac_toes/core/board_factory.rb +11 -0
  10. data/lib/tic_tac_toes/core/game_state.rb +40 -0
  11. data/lib/tic_tac_toes/core/game_state_factory.rb +15 -0
  12. data/lib/tic_tac_toes/core/history.rb +29 -0
  13. data/lib/tic_tac_toes/core/io.rb +93 -0
  14. data/lib/tic_tac_toes/core/move_strategies/easy_ai.rb +13 -0
  15. data/lib/tic_tac_toes/core/move_strategies/hard_ai.rb +89 -0
  16. data/lib/tic_tac_toes/core/move_strategies/human.rb +20 -0
  17. data/lib/tic_tac_toes/core/move_strategies/medium_ai.rb +15 -0
  18. data/lib/tic_tac_toes/core/player.rb +22 -0
  19. data/lib/tic_tac_toes/core/player_factory.rb +30 -0
  20. data/lib/tic_tac_toes/core/rules.rb +66 -0
  21. data/lib/tic_tac_toes/core/strings.rb +106 -0
  22. data/lib/tic_tac_toes/database/pg_wrapper.rb +74 -0
  23. data/lib/tic_tac_toes/ui/adapter.rb +96 -0
  24. data/lib/tic_tac_toes/ui/serializer.rb +0 -0
  25. data/lib/tic_tac_toes/version.rb +1 -1
  26. data/spec/{command_line → tic_tac_toes/command_line}/menu_spec.rb +14 -14
  27. data/spec/{command_line → tic_tac_toes/command_line}/runner_spec.rb +7 -10
  28. data/spec/tic_tac_toes/{board_factory_spec.rb → core/board_factory_spec.rb} +3 -3
  29. data/spec/tic_tac_toes/{board_spec.rb → core/board_spec.rb} +23 -23
  30. data/spec/tic_tac_toes/{game_state_factory_spec.rb → core/game_state_factory_spec.rb} +3 -3
  31. data/spec/tic_tac_toes/core/game_state_spec.rb +129 -0
  32. data/spec/tic_tac_toes/{history_spec.rb → core/history_spec.rb} +3 -3
  33. data/spec/tic_tac_toes/{io_spec.rb → core/io_spec.rb} +7 -7
  34. data/spec/tic_tac_toes/core/move_strategies/easy_ai_spec.rb +19 -0
  35. data/spec/tic_tac_toes/core/move_strategies/hard_ai_spec.rb +121 -0
  36. data/spec/tic_tac_toes/{move_strategies → core/move_strategies}/human_spec.rb +3 -3
  37. data/spec/tic_tac_toes/core/move_strategies/medium_ai_spec.rb +21 -0
  38. data/spec/tic_tac_toes/{player_factory_spec.rb → core/player_factory_spec.rb} +7 -7
  39. data/spec/tic_tac_toes/{player_spec.rb → core/player_spec.rb} +5 -5
  40. data/spec/tic_tac_toes/{rules_spec.rb → core/rules_spec.rb} +34 -34
  41. data/spec/tic_tac_toes/{strings_spec.rb → core/strings_spec.rb} +8 -8
  42. data/spec/{database → tic_tac_toes/database}/pg_wrapper_spec.rb +3 -3
  43. data/spec/tic_tac_toes/test_board_generator.rb +17 -0
  44. data/spec/{ui → tic_tac_toes/ui}/adapter_spec.rb +13 -13
  45. data/spec/tic_tac_toes/ui/serializer_spec.rb +0 -0
  46. metadata +64 -61
  47. data/lib/command_line/menu.rb +0 -53
  48. data/lib/command_line/prompt.rb +0 -27
  49. data/lib/command_line/runner.rb +0 -37
  50. data/lib/database/pg_wrapper.rb +0 -72
  51. data/lib/tic_tac_toes/board.rb +0 -62
  52. data/lib/tic_tac_toes/board_factory.rb +0 -9
  53. data/lib/tic_tac_toes/game_state.rb +0 -27
  54. data/lib/tic_tac_toes/game_state_factory.rb +0 -14
  55. data/lib/tic_tac_toes/history.rb +0 -27
  56. data/lib/tic_tac_toes/io.rb +0 -91
  57. data/lib/tic_tac_toes/move_strategies/easy_ai.rb +0 -11
  58. data/lib/tic_tac_toes/move_strategies/hard_ai.rb +0 -87
  59. data/lib/tic_tac_toes/move_strategies/human.rb +0 -18
  60. data/lib/tic_tac_toes/move_strategies/medium_ai.rb +0 -13
  61. data/lib/tic_tac_toes/player.rb +0 -20
  62. data/lib/tic_tac_toes/player_factory.rb +0 -28
  63. data/lib/tic_tac_toes/rules.rb +0 -64
  64. data/lib/tic_tac_toes/strings.rb +0 -104
  65. data/lib/ui/adapter.rb +0 -94
  66. data/spec/test_board_generator.rb +0 -15
  67. data/spec/tic_tac_toes/game_state_spec.rb +0 -66
  68. data/spec/tic_tac_toes/move_strategies/easy_ai_spec.rb +0 -19
  69. data/spec/tic_tac_toes/move_strategies/hard_ai_spec.rb +0 -121
  70. data/spec/tic_tac_toes/move_strategies/medium_ai_spec.rb +0 -21
  71. /data/lib/{tasks → tic_tac_toes/tasks}/destroy_databases.rake +0 -0
  72. /data/lib/{tasks → tic_tac_toes/tasks}/set_up_databases.rake +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 46bd6a4b98b10769789684336cfc57eca3541e20
4
- data.tar.gz: 95409188361e77e87b4eb687748dfeb34f7d2dc2
3
+ metadata.gz: 085e4d7d1fd5316dd0a28ca154203ae9411e3c2f
4
+ data.tar.gz: ef98016568277620377b83e02aa0ea58827a379b
5
5
  SHA512:
6
- metadata.gz: 52ed4db96736d0fcc5ab1ce93d81acad04244aa77a7bdec19fd88021aaac87a76db638027419a5332a7aee59f2abe2f4734b5d6f5c66d2666b3f8d1f637f2a7c
7
- data.tar.gz: 8c127e6cdec642f082c17f87f2626125343d2dafd66d0d5436fca4ceb088a8e0c198326339237387bec3589a3513945fd8ed7852dd67059ffbdb20c5df1d98cf
6
+ metadata.gz: 688112f80e57889cbd0cf27e190f3f2a7a8e41dc6823ef9ee1d47bf622cd5f554c52b8684c4332c97d838ea923c5b32cb2f0687f92f9a2d3ff9169c5730ba308
7
+ data.tar.gz: ccf022fc972444762499b6ac6a9450dc91ae27be064d24692c11cbdfe232814ff6ad18425e98f56c052c6649aa8de7a0a3c56b1f664fea422ec3aee2db4af24a
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- tic_tac_toes (0.0.7)
4
+ tic_tac_toes (0.0.8)
5
5
  pg
6
6
 
7
7
  GEM
data/Rakefile CHANGED
@@ -1,4 +1,4 @@
1
1
  require 'bundler/gem_tasks'
2
2
 
3
- load './lib/tasks/set_up_databases.rake'
4
- load './lib/tasks/destroy_databases.rake'
3
+ load './lib/tic_tac_toes/tasks/set_up_databases.rake'
4
+ load './lib/tic_tac_toes/tasks/destroy_databases.rake'
data/bin/tic_tac_toes CHANGED
@@ -2,34 +2,30 @@
2
2
 
3
3
  $LOAD_PATH << File.expand_path(File.dirname(__FILE__) + '/../lib')
4
4
 
5
- require 'command_line/prompt'
6
- require 'tic_tac_toes/io'
5
+ require 'tic_tac_toes/command_line/prompt'
6
+ require 'tic_tac_toes/core/io'
7
7
 
8
- require 'tic_tac_toes/board_factory'
9
- require 'tic_tac_toes/player_factory'
10
- require 'command_line/menu'
8
+ require 'tic_tac_toes/core/board_factory'
9
+ require 'tic_tac_toes/core/player_factory'
10
+ require 'tic_tac_toes/command_line/menu'
11
11
 
12
- require 'tic_tac_toes/rules'
12
+ require 'tic_tac_toes/database/pg_wrapper'
13
+ require 'tic_tac_toes/core/history'
14
+ require 'tic_tac_toes/core/game_state_factory'
13
15
 
14
- require 'database/pg_wrapper'
15
- require 'tic_tac_toes/history'
16
- require 'tic_tac_toes/game_state_factory'
17
-
18
- require 'command_line/runner'
16
+ require 'tic_tac_toes/command_line/runner'
19
17
 
20
18
  database = "tic_tac_toes"
21
19
 
22
- prompt = CommandLine::Prompt
23
- io = TicTacToes::IO.new(prompt)
24
-
25
- board_factory = TicTacToes::BoardFactory.new
26
- player_factory = TicTacToes::PlayerFactory.new(io)
27
- menu = CommandLine::Menu.new(io, board_factory, player_factory)
20
+ prompt = TicTacToes::CommandLine::Prompt
21
+ io = TicTacToes::Core::IO.new(prompt)
28
22
 
29
- database_wrapper = Database::PGWrapper.new(database)
30
- history = TicTacToes::History.new(database_wrapper)
31
- game_state_factory = TicTacToes::GameStateFactory.new(history)
23
+ board_factory = TicTacToes::Core::BoardFactory.new
24
+ player_factory = TicTacToes::Core::PlayerFactory.new(io)
25
+ menu = TicTacToes::CommandLine::Menu.new(io, board_factory, player_factory)
32
26
 
33
- rules = TicTacToes::Rules
27
+ database_wrapper = TicTacToes::Database::PGWrapper.new(database)
28
+ history = TicTacToes::Core::History.new(database_wrapper)
29
+ game_state_factory = TicTacToes::Core::GameStateFactory.new(history)
34
30
 
35
- CommandLine::Runner.new(io, menu, game_state_factory, rules).run
31
+ TicTacToes::CommandLine::Runner.new(io, menu, game_state_factory).run
@@ -0,0 +1,55 @@
1
+ require 'tic_tac_toes/core/rules'
2
+
3
+ module TicTacToes
4
+ module CommandLine
5
+ class Menu
6
+ def initialize(io, board_factory, player_factory)
7
+ @io = io
8
+ @board_factory = board_factory
9
+ @player_factory = player_factory
10
+ end
11
+
12
+ def get_board
13
+ @board_factory.generate_board(get_row_size)
14
+ end
15
+
16
+ def get_players
17
+ taken_tokens = []
18
+ human_token = get_token(:human, taken_tokens)
19
+ taken_tokens << human_token
20
+ computer_token = get_token(:computer, taken_tokens)
21
+ difficulty = get_difficulty
22
+
23
+ human_player = @player_factory.generate_human_player(human_token)
24
+ computer_player = @player_factory.generate_computer_player(computer_token, difficulty)
25
+ [human_player, computer_player]
26
+ end
27
+
28
+ private
29
+
30
+ def get_row_size
31
+ loop do
32
+ row_size = @io.get_row_size
33
+ break row_size if Core::Rules.row_size_valid?(row_size)
34
+ @io.invalid_row_size_error
35
+ end
36
+ end
37
+
38
+ def get_token(player, taken_tokens)
39
+ loop do
40
+ token = @io.get_token(player)
41
+ break token if Core::Rules.token_valid?(token, taken_tokens)
42
+ @io.invalid_token_error
43
+ end
44
+ end
45
+
46
+ def get_difficulty
47
+ loop do
48
+ difficulty = @io.get_difficulty
49
+ break difficulty if Core::Rules.difficulty_valid?(difficulty)
50
+ @io.invalid_difficulty_error
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,29 @@
1
+ module TicTacToes
2
+ module CommandLine
3
+ module Prompt
4
+ def self.solicit_input
5
+ gets.chomp
6
+ end
7
+
8
+ def self.display(message)
9
+ puts message
10
+ end
11
+
12
+ def self.display_red(message)
13
+ puts red(message)
14
+ end
15
+
16
+ def self.red(message)
17
+ colorize(message, 31)
18
+ end
19
+
20
+ def self.blue(message)
21
+ colorize(message, 34)
22
+ end
23
+
24
+ def self.colorize(message, color_code)
25
+ "\e[#{color_code}m#{message}\e[0m"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,38 @@
1
+ module TicTacToes
2
+ module CommandLine
3
+ class Runner
4
+ def initialize(io, menu, game_state_factory)
5
+ @io = io
6
+ @menu = menu
7
+ @game_state_factory = game_state_factory
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 game_state.game_over?
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 = game_state.determine_winner
32
+ game_state.game_over(winner)
33
+
34
+ @io.game_over_notification(winner)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,64 @@
1
+ module TicTacToes
2
+ module Core
3
+ class Board
4
+ attr_reader :row_size, :size, :spaces
5
+
6
+ def initialize(row_size: 3)
7
+ @row_size = row_size
8
+ @size = @row_size**2
9
+ @spaces = Array.new(@size)
10
+ end
11
+
12
+ def place(player, space)
13
+ @spaces[space] = player if valid?(space)
14
+ end
15
+
16
+ def space(space)
17
+ @spaces[space]
18
+ end
19
+
20
+ def open_spaces
21
+ open_spaces = []
22
+
23
+ @spaces.each_with_index do |space, index|
24
+ open_spaces << index if space.nil?
25
+ end
26
+
27
+ open_spaces
28
+ end
29
+
30
+ def rows
31
+ @spaces.each_slice(@row_size).to_a
32
+ end
33
+
34
+ def columns
35
+ rows.transpose
36
+ end
37
+
38
+ def diagonals
39
+ back_diagonal, front_diagonal = [], []
40
+
41
+ rows.each_with_index do |row, index|
42
+ back_diagonal << row[index]
43
+ front_diagonal << row[@row_size - (index + 1)]
44
+ end
45
+
46
+ [back_diagonal, front_diagonal]
47
+ end
48
+
49
+ def full?
50
+ @spaces.all? { |space| !space.nil? }
51
+ end
52
+
53
+ private
54
+
55
+ def valid?(space)
56
+ space_empty = @spaces[space].nil?
57
+ board_range = 0..(@size - 1)
58
+ on_the_board = board_range.include? space
59
+
60
+ space_empty && on_the_board
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,11 @@
1
+ require 'tic_tac_toes/core/board'
2
+
3
+ module TicTacToes
4
+ module Core
5
+ class BoardFactory
6
+ def generate_board(row_size)
7
+ Board.new(row_size: row_size)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,40 @@
1
+ require 'tic_tac_toes/core/rules'
2
+
3
+ module TicTacToes
4
+ module Core
5
+ class GameState
6
+ attr_reader :board, :players
7
+
8
+ def initialize(board, players, history)
9
+ @board = board
10
+ @players = players
11
+ @history = history
12
+ @rules = Rules
13
+
14
+ @history.record_board_size(@board.size)
15
+ end
16
+
17
+ def current_player
18
+ @players.first
19
+ end
20
+
21
+ def turn_over(move)
22
+ @history.record_move(move)
23
+ @players.rotate!
24
+ end
25
+
26
+ def game_over(winner)
27
+ @history.record_winner(winner)
28
+ @history.persist
29
+ end
30
+
31
+ def game_over?
32
+ @rules.game_over?(@board, @players)
33
+ end
34
+
35
+ def determine_winner
36
+ @rules.determine_winner(@board, @players)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,15 @@
1
+ require 'tic_tac_toes/core/game_state'
2
+
3
+ module TicTacToes
4
+ module Core
5
+ class GameStateFactory
6
+ def initialize(history)
7
+ @history = history
8
+ end
9
+
10
+ def generate_game_state(board, players)
11
+ GameState.new(board, players, @history)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,29 @@
1
+ module TicTacToes
2
+ module Core
3
+ class History
4
+ attr_reader :board_size, :moves, :winner
5
+
6
+ def initialize(database_wrapper)
7
+ @database_wrapper = database_wrapper
8
+ end
9
+
10
+ def record_board_size(size)
11
+ @board_size = size
12
+ end
13
+
14
+ def record_move(move)
15
+ @moves ||= []
16
+ @moves << move
17
+ end
18
+
19
+ def record_winner(winner)
20
+ winner = "Draw" if winner.nil?
21
+ @winner = winner
22
+ end
23
+
24
+ def persist
25
+ @database_wrapper.record_game_history(self)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,93 @@
1
+ require 'tic_tac_toes/core/strings'
2
+
3
+ module TicTacToes
4
+ module Core
5
+ class IO
6
+ def initialize(io_strategy)
7
+ @io_strategy = io_strategy
8
+ end
9
+
10
+ def get_row_size
11
+ row_size_solicitation
12
+
13
+ Integer(@io_strategy.solicit_input)
14
+ rescue ArgumentError
15
+ not_an_integer_error
16
+ get_row_size
17
+ end
18
+
19
+ def get_token(player)
20
+ token_solicitation(player)
21
+ @io_strategy.solicit_input
22
+ end
23
+
24
+ def get_difficulty
25
+ difficulty_solicitation
26
+ @io_strategy.solicit_input.downcase.to_sym
27
+ end
28
+
29
+ def draw_board(board)
30
+ @io_strategy.display(Strings.board(board))
31
+ end
32
+
33
+ def invalid_row_size_error
34
+ @io_strategy.display_red(Strings::INVALID_ROW_SIZE)
35
+ end
36
+
37
+ def invalid_token_error
38
+ @io_strategy.display_red(Strings::INVALID_TOKEN)
39
+ end
40
+
41
+ def invalid_difficulty_error
42
+ @io_strategy.display_red(Strings::INVALID_DIFFICULTY)
43
+ end
44
+
45
+ def invalid_move_error
46
+ @io_strategy.display_red(Strings::INVALID_MOVE)
47
+ end
48
+
49
+ def thinking_notification
50
+ @io_strategy.display_red(Strings::THINKING)
51
+ end
52
+
53
+ def game_over_notification(winner)
54
+ winner = "Nobody" if winner.nil?
55
+ @io_strategy.display(Strings.game_over_notification(winner))
56
+ end
57
+
58
+ def move_solicitation
59
+ @io_strategy.display(Strings::MOVE_SOLICITATION)
60
+ end
61
+
62
+ def not_an_integer_error
63
+ @io_strategy.display_red(Strings::NOT_AN_INTEGER)
64
+ end
65
+
66
+ def solicit_input
67
+ @io_strategy.solicit_input
68
+ end
69
+
70
+ def red(message)
71
+ @io_strategy.red(message)
72
+ end
73
+
74
+ def blue(message)
75
+ @io_strategy.blue(message)
76
+ end
77
+
78
+ private
79
+
80
+ def row_size_solicitation
81
+ @io_strategy.display(Strings::ROW_SIZE_SOLICITATION)
82
+ end
83
+
84
+ def token_solicitation(player)
85
+ @io_strategy.display(Strings.token_solicitation(player))
86
+ end
87
+
88
+ def difficulty_solicitation
89
+ @io_strategy.display(Strings::DIFFICULTY_SOLICITATION)
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,13 @@
1
+ require 'tic_tac_toes/core/board'
2
+
3
+ module TicTacToes
4
+ module Core
5
+ module MoveStrategies
6
+ module EasyAI
7
+ def self.move(board, players)
8
+ board.open_spaces.sample
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,89 @@
1
+ require 'tic_tac_toes/core/rules'
2
+
3
+ module TicTacToes
4
+ module Core
5
+ module MoveStrategies
6
+ module HardAI
7
+ def self.move(board, players)
8
+ return second_move(board) if nine_board_second_move?(board)
9
+
10
+ open_spaces = Hash[board.open_spaces.map { |space| [space, nil] }]
11
+
12
+ open_spaces.each do |space, score|
13
+ score = minimax(generate_board(players.first, space, board), :min, players)
14
+ open_spaces[space] = score
15
+ end
16
+
17
+ best_score = open_spaces.values.max
18
+ open_spaces.each { |space, score| return space if score == best_score }
19
+ end
20
+
21
+ private
22
+
23
+ def self.minimax(board, current_player, players)
24
+ return score(board, players) if Rules.game_over?(board, players)
25
+
26
+ if current_player == :max
27
+ best_score = -1
28
+ board.open_spaces.each do |space|
29
+ score = minimax(generate_board(players.first, space, board), :min, players)
30
+ best_score = [best_score, score].max
31
+ end
32
+ best_score
33
+
34
+ elsif current_player == :min
35
+ best_score = 1
36
+ board.open_spaces.each do |space|
37
+ score = minimax(generate_board(players.last, space, board), :max, players)
38
+ best_score = [best_score, score].min
39
+ end
40
+ best_score
41
+ end
42
+ end
43
+
44
+ def self.generate_board(player, space, board)
45
+ new_board = Marshal.load(Marshal.dump(board))
46
+ new_board.place(player, space)
47
+ new_board
48
+ end
49
+
50
+ def self.score(board, players)
51
+ own_token, opponent_token = players.first.token, players.last.token
52
+
53
+ case Rules.determine_winner(board, players)
54
+ when own_token
55
+ 1
56
+ when opponent_token
57
+ -1
58
+ else
59
+ 0
60
+ end
61
+ end
62
+
63
+ def self.nine_board_second_move?(board)
64
+ board.size == 9 && board.open_spaces.count == 8
65
+ end
66
+
67
+ def self.second_move(board)
68
+ if nine_board_corner_occupied(board) || nine_board_side_occupied(board)
69
+ 4
70
+ elsif nine_board_center_occupied(board)
71
+ 0
72
+ end
73
+ end
74
+
75
+ def self.nine_board_corner_occupied(board)
76
+ board.space(0) || board.space(2) || board.space(6) || board.space(8)
77
+ end
78
+
79
+ def self.nine_board_side_occupied(board)
80
+ board.space(1) || board.space(3) || board.space(5) || board.space(7)
81
+ end
82
+
83
+ def self.nine_board_center_occupied(board)
84
+ board.space(4) ? true : false
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,20 @@
1
+ module TicTacToes
2
+ module Core
3
+ module MoveStrategies
4
+ class Human
5
+ def initialize(io)
6
+ @io = io
7
+ end
8
+
9
+ def move(board, players)
10
+ @io.move_solicitation
11
+
12
+ Integer(@io.solicit_input)
13
+ rescue ArgumentError
14
+ @io.not_an_integer_error
15
+ move(board, players)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ require 'tic_tac_toes/core/move_strategies/easy_ai'
2
+ require 'tic_tac_toes/core/move_strategies/hard_ai'
3
+
4
+ module TicTacToes
5
+ module Core
6
+ module MoveStrategies
7
+ module MediumAI
8
+ def self.move(board, players)
9
+ move_strategy = [EasyAI, HardAI, HardAI].sample
10
+ move_strategy.move(board, players)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,22 @@
1
+ module TicTacToes
2
+ module Core
3
+ class Player
4
+ attr_reader :move_strategy, :token, :needs_to_think
5
+
6
+ def initialize(move_strategy, token, needs_to_think, io)
7
+ @move_strategy = move_strategy
8
+ @token = token
9
+ @needs_to_think = needs_to_think
10
+ @io = io
11
+ end
12
+
13
+ def place_and_return_move(board, players)
14
+ loop do
15
+ space = @move_strategy.move(board, players)
16
+ break [@token, space] if board.place(self, space)
17
+ @io.invalid_move_error
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ require 'tic_tac_toes/core/move_strategies/human'
2
+ require 'tic_tac_toes/core/move_strategies/easy_ai'
3
+ require 'tic_tac_toes/core/move_strategies/medium_ai'
4
+ require 'tic_tac_toes/core/move_strategies/hard_ai'
5
+ require 'tic_tac_toes/core/player'
6
+
7
+ module TicTacToes
8
+ module Core
9
+ class PlayerFactory
10
+ def initialize(io)
11
+ @io = io
12
+ end
13
+
14
+ AIS = {
15
+ easy: ::TicTacToes::Core::MoveStrategies::EasyAI,
16
+ medium: ::TicTacToes::Core::MoveStrategies::MediumAI,
17
+ hard: ::TicTacToes::Core::MoveStrategies::HardAI }
18
+
19
+ def generate_human_player(token)
20
+ needs_to_think = false
21
+ Player.new(TicTacToes::Core::MoveStrategies::Human.new(@io), token, needs_to_think, @io)
22
+ end
23
+
24
+ def generate_computer_player(token, difficulty)
25
+ needs_to_think = true
26
+ Player.new(AIS[difficulty], token, needs_to_think, @io)
27
+ end
28
+ end
29
+ end
30
+ end