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.
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