bashrw_ttt 0.1.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.
Files changed (130) hide show
  1. data/bin/play.rb +10 -0
  2. data/lib/ttt/ai.rb +33 -0
  3. data/lib/ttt/ai_easy.rb +17 -0
  4. data/lib/ttt/ai_hard.rb +21 -0
  5. data/lib/ttt/ai_medium.rb +21 -0
  6. data/lib/ttt/ai_medium_back.rb +58 -0
  7. data/lib/ttt/board.rb +37 -0
  8. data/lib/ttt/config_helper.rb +53 -0
  9. data/lib/ttt/config_options.rb +27 -0
  10. data/lib/ttt/context.rb +214 -0
  11. data/lib/ttt/four_by_four.rb +62 -0
  12. data/lib/ttt/game.rb +94 -0
  13. data/lib/ttt/game_history.rb +37 -0
  14. data/lib/ttt/game_interactor.rb +126 -0
  15. data/lib/ttt/human.rb +12 -0
  16. data/lib/ttt/interfaces/cli/lib/cli/board_selection.rb +34 -0
  17. data/lib/ttt/interfaces/cli/lib/cli/cli_game.rb +186 -0
  18. data/lib/ttt/interfaces/cli/lib/cli/cli_presenter.rb +216 -0
  19. data/lib/ttt/interfaces/cli/lib/cli/clio.rb +17 -0
  20. data/lib/ttt/interfaces/cli/lib/cli/play_again.rb +38 -0
  21. data/lib/ttt/interfaces/cli/lib/cli/player_selection.rb +43 -0
  22. data/lib/ttt/interfaces/cli/lib/cli/selection.rb +8 -0
  23. data/lib/ttt/interfaces/cli/lib/cli/walk_history.rb +48 -0
  24. data/lib/ttt/interfaces/cli/spec/cli/board_selection_spec.rb +29 -0
  25. data/lib/ttt/interfaces/cli/spec/cli/cli_game_spec.rb +376 -0
  26. data/lib/ttt/interfaces/cli/spec/cli/cli_presenter_spec.rb +147 -0
  27. data/lib/ttt/interfaces/cli/spec/cli/play_again_spec.rb +39 -0
  28. data/lib/ttt/interfaces/cli/spec/cli/player_selection_spec.rb +62 -0
  29. data/lib/ttt/interfaces/cli/spec/cli/walk_history_spec.rb +115 -0
  30. data/lib/ttt/interfaces/cli/spec/spec_helper.rb +14 -0
  31. data/lib/ttt/interfaces/limelight/four_by_four/props.rb +1 -0
  32. data/lib/ttt/interfaces/limelight/four_by_four/styles.rb +74 -0
  33. data/lib/ttt/interfaces/limelight/four_by_four_finished/props.rb +1 -0
  34. data/lib/ttt/interfaces/limelight/four_by_four_finished/styles.rb +74 -0
  35. data/lib/ttt/interfaces/limelight/game_list/players/load_button.rb +10 -0
  36. data/lib/ttt/interfaces/limelight/game_list/props.rb +17 -0
  37. data/lib/ttt/interfaces/limelight/game_list/styles.rb +8 -0
  38. data/lib/ttt/interfaces/limelight/main_menu/players/exit_ttt.rb +3 -0
  39. data/lib/ttt/interfaces/limelight/main_menu/players/load_game.rb +3 -0
  40. data/lib/ttt/interfaces/limelight/main_menu/players/new_game.rb +3 -0
  41. data/lib/ttt/interfaces/limelight/main_menu/props.rb +17 -0
  42. data/lib/ttt/interfaces/limelight/new_game/players/board_type.rb +1 -0
  43. data/lib/ttt/interfaces/limelight/new_game/players/new_game.rb +5 -0
  44. data/lib/ttt/interfaces/limelight/new_game/players/player_type.rb +1 -0
  45. data/lib/ttt/interfaces/limelight/new_game/players/setup_button.rb +21 -0
  46. data/lib/ttt/interfaces/limelight/new_game/props.rb +21 -0
  47. data/lib/ttt/interfaces/limelight/new_game/styles.rb +23 -0
  48. data/lib/ttt/interfaces/limelight/partials/board_partial.rb +14 -0
  49. data/lib/ttt/interfaces/limelight/partials/menu_button_partial.rb +5 -0
  50. data/lib/ttt/interfaces/limelight/partials/move_history_partial.rb +6 -0
  51. data/lib/ttt/interfaces/limelight/partials/title_history_partial.rb +10 -0
  52. data/lib/ttt/interfaces/limelight/players/board.rb +3 -0
  53. data/lib/ttt/interfaces/limelight/players/game.rb +4 -0
  54. data/lib/ttt/interfaces/limelight/players/generic_move_history.rb +5 -0
  55. data/lib/ttt/interfaces/limelight/players/generic_player.rb +8 -0
  56. data/lib/ttt/interfaces/limelight/players/left_button.rb +3 -0
  57. data/lib/ttt/interfaces/limelight/players/main_menu.rb +3 -0
  58. data/lib/ttt/interfaces/limelight/players/right_button.rb +3 -0
  59. data/lib/ttt/interfaces/limelight/playscripts/game_playscript.rb +169 -0
  60. data/lib/ttt/interfaces/limelight/production.rb +14 -0
  61. data/lib/ttt/interfaces/limelight/spec/game_playscript_spec.rb +218 -0
  62. data/lib/ttt/interfaces/limelight/spec/main_menu/main_menu_spec.rb +38 -0
  63. data/lib/ttt/interfaces/limelight/spec/new_game/new_game_spec.rb +45 -0
  64. data/lib/ttt/interfaces/limelight/spec/spec_helper.rb +8 -0
  65. data/lib/ttt/interfaces/limelight/stages.rb +6 -0
  66. data/lib/ttt/interfaces/limelight/styles.rb +119 -0
  67. data/lib/ttt/interfaces/limelight/three_by_three/props.rb +1 -0
  68. data/lib/ttt/interfaces/limelight/three_by_three/styles.rb +39 -0
  69. data/lib/ttt/interfaces/limelight/three_by_three_by_three/props.rb +1 -0
  70. data/lib/ttt/interfaces/limelight/three_by_three_by_three/styles.rb +119 -0
  71. data/lib/ttt/interfaces/limelight/three_by_three_by_three_finished/props.rb +1 -0
  72. data/lib/ttt/interfaces/limelight/three_by_three_by_three_finished/styles.rb +119 -0
  73. data/lib/ttt/interfaces/limelight/three_by_three_finished/props.rb +1 -0
  74. data/lib/ttt/interfaces/limelight/three_by_three_finished/styles.rb +39 -0
  75. data/lib/ttt/interfaces/rails/app/controllers/application_controller.rb +3 -0
  76. data/lib/ttt/interfaces/rails/app/controllers/ttt_games_controller.rb +86 -0
  77. data/lib/ttt/interfaces/rails/app/helpers/application_helper.rb +2 -0
  78. data/lib/ttt/interfaces/rails/app/models/ttt_game.rb +34 -0
  79. data/lib/ttt/interfaces/rails/app/presenters/web_game_presenter.rb +159 -0
  80. data/lib/ttt/interfaces/rails/autotest/discover.rb +1 -0
  81. data/lib/ttt/interfaces/rails/config/application.rb +67 -0
  82. data/lib/ttt/interfaces/rails/config/boot.rb +6 -0
  83. data/lib/ttt/interfaces/rails/config/environment.rb +5 -0
  84. data/lib/ttt/interfaces/rails/config/environments/development.rb +37 -0
  85. data/lib/ttt/interfaces/rails/config/environments/production.rb +67 -0
  86. data/lib/ttt/interfaces/rails/config/environments/test.rb +37 -0
  87. data/lib/ttt/interfaces/rails/config/initializers/backtrace_silencers.rb +7 -0
  88. data/lib/ttt/interfaces/rails/config/initializers/inflections.rb +15 -0
  89. data/lib/ttt/interfaces/rails/config/initializers/mime_types.rb +5 -0
  90. data/lib/ttt/interfaces/rails/config/initializers/secret_token.rb +7 -0
  91. data/lib/ttt/interfaces/rails/config/initializers/session_store.rb +8 -0
  92. data/lib/ttt/interfaces/rails/config/initializers/wrap_parameters.rb +14 -0
  93. data/lib/ttt/interfaces/rails/config/routes.rb +49 -0
  94. data/lib/ttt/interfaces/rails/db/schema.rb +28 -0
  95. data/lib/ttt/interfaces/rails/db/seeds.rb +7 -0
  96. data/lib/ttt/interfaces/rails/spec/controllers/ttt_games_controller_spec.rb +211 -0
  97. data/lib/ttt/interfaces/rails/spec/models/ttt_game_spec.rb +76 -0
  98. data/lib/ttt/interfaces/rails/spec/presenters/web_game_presenter_spec.rb +52 -0
  99. data/lib/ttt/interfaces/rails/spec/spec_helper.rb +13 -0
  100. data/lib/ttt/interfaces/web_interface/html_generator.rb +76 -0
  101. data/lib/ttt/interfaces/web_interface/web_game_presenter.rb +0 -0
  102. data/lib/ttt/minimax.rb +46 -0
  103. data/lib/ttt/move_history.rb +10 -0
  104. data/lib/ttt/move_traverser.rb +49 -0
  105. data/lib/ttt/player.rb +15 -0
  106. data/lib/ttt/riak_db.rb +53 -0
  107. data/lib/ttt/setup.rb +59 -0
  108. data/lib/ttt/three_by_three.rb +35 -0
  109. data/lib/ttt/three_by_three_by_three.rb +37 -0
  110. data/spec/spec_helper.rb +22 -0
  111. data/spec/ttt/ai_easy_spec.rb +15 -0
  112. data/spec/ttt/ai_hard_spec.rb +76 -0
  113. data/spec/ttt/ai_medium_spec.rb +65 -0
  114. data/spec/ttt/ai_spec.rb +50 -0
  115. data/spec/ttt/board_spec.rb +96 -0
  116. data/spec/ttt/config_helper_spec.rb +54 -0
  117. data/spec/ttt/context_spec.rb +398 -0
  118. data/spec/ttt/four_by_four_spec.rb +112 -0
  119. data/spec/ttt/game_history_spec.rb +41 -0
  120. data/spec/ttt/game_interactor_spec.rb +197 -0
  121. data/spec/ttt/game_spec.rb +246 -0
  122. data/spec/ttt/human_spec.rb +18 -0
  123. data/spec/ttt/move_history_spec.rb +13 -0
  124. data/spec/ttt/move_traverser_spec.rb +75 -0
  125. data/spec/ttt/player_spec.rb +13 -0
  126. data/spec/ttt/riak_db_spec.rb +64 -0
  127. data/spec/ttt/setup_spec.rb +63 -0
  128. data/spec/ttt/three_by_three_by_three_spec.rb +130 -0
  129. data/spec/ttt/three_by_three_spec.rb +110 -0
  130. metadata +238 -0
@@ -0,0 +1,46 @@
1
+ module TTT
2
+ module TTT::Minimax
3
+ def minimax(max_player = true, ply = 0, alpha = -1000, beta = 1000)
4
+ return(board.winner? ? winning_score(max_player, ply) : 0) if base_case_satisfied?
5
+ ab_value = max_player ? alpha : beta
6
+ return ab_value if ply >= max_ply
7
+
8
+ ab_value, best_move = gen_score_game_tree(max_player, ply, alpha, beta, ab_value)
9
+ return(ply == 0 ? best_move : ab_value)
10
+ end
11
+
12
+ def gen_score_game_tree(max_player, ply, alpha, beta, ab_value)
13
+ best_move = 0
14
+ available_moves.each do |index|
15
+ board[][index] = mark_curr_player_side(max_player)
16
+ score = minimax(!max_player, ply + 1, alpha, beta)
17
+ alpha, beta, ab_value, best_move = eval_score(max_player, index, score, alpha, beta, ab_value, best_move)
18
+ undo_move(index)
19
+ break if alpha_beta_swapped?(alpha, beta)
20
+ end
21
+ [ab_value, best_move]
22
+ end
23
+
24
+ def eval_score(max_player, index, score, alpha, beta, ab_value, best_move)
25
+ best_move, alpha, ab_value = [index, score, score] if max_player && score > ab_value
26
+ beta, ab_value = [score, score] if !max_player && score < ab_value
27
+ [alpha, beta, ab_value, best_move]
28
+ end
29
+
30
+ def alpha_beta_swapped?(alpha, beta)
31
+ alpha >= beta
32
+ end
33
+
34
+ def base_case_satisfied?
35
+ board.winner? || board.draw_game?
36
+ end
37
+
38
+ def winning_score(max_player, ply)
39
+ max_player ? (-1000 + ply) : (1000 - ply)
40
+ end
41
+
42
+ def mark_curr_player_side(max_player)
43
+ max_player ? side : opposite_side(side)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,10 @@
1
+ module TTT
2
+ class MoveHistory
3
+ attr_reader :side, :move
4
+ def initialize(options)
5
+ @side = options.fetch(:side)
6
+ @move = options.fetch(:move)
7
+ end
8
+ end
9
+ end
10
+
@@ -0,0 +1,49 @@
1
+ require 'yaml'
2
+
3
+ module TTT
4
+ class MoveTraverser
5
+ attr_accessor :game_history, :move_index, :current_index
6
+ def initialize(game_history)
7
+ self.move_index = 0
8
+ self.game_history = game_history
9
+ self.current_index = 0
10
+ end
11
+
12
+ def initialize_history
13
+ self.move_index = max_length
14
+ end
15
+
16
+ def max_length
17
+ game_history.history.length
18
+ end
19
+
20
+ def adjust_move_index(distance)
21
+ if move_index + distance >= 0 && move_index + distance <= max_length
22
+ self.move_index += distance
23
+ elsif move_index + distance < 0
24
+ self.move_index = 0
25
+ else
26
+ self.move_index = max_length
27
+ end
28
+ end
29
+
30
+ def history_board_builder(board, move_number_limit = move_index)
31
+ total_moves = game_history.history.length
32
+ clone_board = YAML.load(YAML.dump(board))
33
+ clean_board(clone_board)
34
+ clone_history = YAML.load(YAML.dump(game_history.history))
35
+
36
+ total_moves.times do |n|
37
+ cur_move = clone_history.shift
38
+ if (n + 1) <= move_number_limit
39
+ clone_board.update(cur_move.move, cur_move.side)
40
+ end
41
+ end
42
+ clone_board.board
43
+ end
44
+
45
+ def clean_board(board)
46
+ board.board.each_index { |index| board.update index, " " }
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,15 @@
1
+ module TTT
2
+ class Player
3
+ attr_accessor :side
4
+ def initialize(options)
5
+ self.side = options.fetch(:side)
6
+ end
7
+
8
+ def move; end
9
+
10
+ def ==(other_player)
11
+ self.class == other_player.class &&
12
+ side == other_player.side
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,53 @@
1
+ require 'riak'
2
+ module TTT
3
+ class RiakDB
4
+ Riak.disable_list_keys_warnings = true
5
+ attr_accessor :client, :bucket, :cur_id
6
+ def initialize(options)
7
+ port = options.fetch(:port, 8098)
8
+ http_backend = options.fetch(:http_backend, :Excon)
9
+ self.bucket = options.fetch(:bucket)
10
+ self.client = Riak::Client.new(:http_port => port)
11
+ self.cur_id = client[bucket].keys.length.to_s
12
+ end
13
+
14
+ def add_game(game)
15
+ self.cur_id = inc_cur_id
16
+ new_game = client[bucket].get_or_new(cur_id)
17
+ new_game.content_type = "text/yaml"
18
+ new_game.data = { :game => game, :id => cur_id }
19
+ new_game.store
20
+ cur_id
21
+ end
22
+
23
+ def game_list
24
+ client[bucket].keys.map(&:to_i).sort.map(&:to_s)
25
+ end
26
+
27
+ def get_game(id)
28
+ begin
29
+ game = client[bucket].get(id.to_s)
30
+ rescue Riak::HTTPFailedRequest
31
+ nil
32
+ else
33
+ game.data[:game]
34
+ end
35
+ end
36
+
37
+ def save_game(id, game)
38
+ riak_game = client[bucket].get(id)
39
+ riak_game.data = { :game => game, :id => id }
40
+ riak_game.store
41
+ end
42
+
43
+ def delete_game(id)
44
+ riak_game = client[bucket].get(id)
45
+ riak_game.delete
46
+ end
47
+
48
+ private
49
+ def inc_cur_id
50
+ (client[bucket].keys.length + 1).to_s
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,59 @@
1
+ require 'ttt/config_helper'
2
+ require 'ttt/game'
3
+
4
+ module TTT
5
+ class Setup
6
+ def players
7
+ ConfigHelper.player_types
8
+ end
9
+
10
+ def boards
11
+ ConfigHelper.board_types
12
+ end
13
+
14
+ def new_game(options)
15
+ Game.new(:player1 => instantiate_player(1, options.fetch(:player1)),
16
+ :player2 => instantiate_player(2, options.fetch(:player2)),
17
+ :board => instantiate_board(options.fetch(:board)),
18
+ :history => instantiate_history)
19
+ end
20
+
21
+ def new_db
22
+ instantiate_db
23
+ end
24
+
25
+ def new_interactor
26
+ instantiate_interactor
27
+ end
28
+
29
+ def db
30
+ ConfigHelper.get_db_const
31
+ end
32
+
33
+ def interactor
34
+ ConfigHelper.get_game_interactor_const
35
+ end
36
+
37
+ private
38
+ def instantiate_interactor
39
+ ConfigHelper.get_game_interactor_const.new(instantiate_db)
40
+ end
41
+
42
+ def instantiate_db
43
+ ConfigHelper.get_db_const.new(:port => ConfigHelper.port, :http_backend => ConfigHelper.http_backend, :bucket => ConfigHelper.bucket)
44
+ end
45
+
46
+ def instantiate_history
47
+ ConfigHelper.get_history_const.new
48
+ end
49
+
50
+ def instantiate_player(player_num, player_type)
51
+ side = player_num == 1 ? "x" : "o"
52
+ ConfigHelper.get_player_const(player_type).new(:side => side)
53
+ end
54
+
55
+ def instantiate_board(board_type)
56
+ ConfigHelper.get_board_const(board_type).new
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,35 @@
1
+ require 'ttt/board'
2
+
3
+ module TTT
4
+ class ThreeByThree < Board
5
+ attr_accessor :win_arr, :win_block
6
+ def initialize
7
+ self.board = Array.new(9, " ")
8
+ self.win_arr = [[0,1,2], [0,4,8], [0,3,6], [1,4,7], [2,5,8], [2,4,6], [3,4,5], [6,7,8]]
9
+ self.win_block = [[0,1,3,4], [1,2,4,5], [3,4,6,7], [4,5,7,8]]
10
+ end
11
+
12
+ def winner?
13
+ win_arr.each do |win_combo|
14
+ return true if board[win_combo[0]] == board[win_combo[1]] &&
15
+ board[win_combo[1]] == board[win_combo[2]] &&
16
+ board[win_combo[0]] != " "
17
+ end
18
+ block_winner?
19
+ end
20
+
21
+ def block_winner?
22
+ win_block.each do |win_combo|
23
+ return true if board[win_combo[0]] == board[win_combo[1]] &&
24
+ board[win_combo[1]] == board[win_combo[2]] &&
25
+ board[win_combo[2]] == board[win_combo[3]] &&
26
+ board[win_combo[0]] != " "
27
+ end
28
+ false
29
+ end
30
+
31
+ def board_type
32
+ "three_by_three"
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,37 @@
1
+ require 'ttt/board'
2
+ module TTT
3
+ class ThreeByThreeByThree < Board
4
+ attr_accessor :win_arr
5
+ def initialize
6
+ self.board = Array.new(27, " ")
7
+
8
+ self.win_arr = [[0,1,2], [0,4,8], [0,3,6], [1,4,7],
9
+ [2,5,8], [2,4,6], [3,4,5], [6,7,8],
10
+ [9,10,11], [12,13,14], [15,16,17],
11
+ [9,12,15], [10,13,16], [11,14,17],
12
+ [9,13,17], [11,13,15], [18,19,20],
13
+ [21,22,23], [24,25,26], [18,21,24],
14
+ [19,22,25], [20,23,26], [18,22,26],
15
+ [20,22,24], [0,9,18], [1,10,19], [2,11,20],
16
+ [3,12,21], [4,13,22], [5,14,23], [6,15,24],
17
+ [7,16,25], [8,17,26], [0,13,26], [2,13,24],
18
+ [2,14,26], [0,12,24], [1,13,25], [6,13,20],
19
+ [8,13,18], [3,13,23], [5,13,21], [6,16,26],
20
+ [7,13,19], [8,16,24], [0,10,20], [2,10,18],
21
+ [6,12,18], [8,14,20]]
22
+ end
23
+
24
+ def winner?
25
+ win_arr.each do |win_combo|
26
+ return true if board[win_combo[0]] == board[win_combo[1]] &&
27
+ board[win_combo[1]] == board[win_combo[2]] &&
28
+ board[win_combo[0]] != " "
29
+ end
30
+ false
31
+ end
32
+
33
+ def board_type
34
+ "three_by_three_by_three"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,22 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ #require 'simplecov'
4
+ #SimpleCov.start
5
+ require 'ttt/ai'
6
+ require 'ttt/player'
7
+ require 'ttt/game'
8
+ require 'ttt/board'
9
+ require 'ttt/game_history'
10
+ require 'ttt/ai_easy'
11
+ require 'ttt/ai_medium'
12
+ require 'ttt/ai_hard'
13
+ require 'ttt/four_by_four'
14
+ require 'ttt/three_by_three'
15
+ require 'ttt/three_by_three_by_three'
16
+ require 'ttt/human'
17
+ require 'ttt/riak_db'
18
+ require 'ttt/setup'
19
+ require 'ttt/config_helper'
20
+ require 'ttt/move_traverser'
21
+ require 'ttt/game_interactor'
22
+ require 'ttt/context'
@@ -0,0 +1,15 @@
1
+ require 'spec_helper'
2
+
3
+ module TTT
4
+ describe AIEasy do
5
+ let(:ai) { AIEasy.new(side: "x") }
6
+ let(:board) { ThreeByThree.new }
7
+
8
+ describe "#minimax" do
9
+ it "returns a random available move from the game board" do
10
+ board[] = ["x", "o", " ", " ", " ", " ", "o", "x", "o" ]
11
+ [2, 3, 4, 5].include? ai.move(board: board)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+
3
+ module TTT
4
+ describe AIHard do
5
+ let(:ai) { AIHard.new(side: "o") }
6
+ let(:board) { ThreeByThree.new }
7
+
8
+ describe "#minimax" do
9
+ it "takes a winning move when one is immediately avaialable" do
10
+ #situation: o to move
11
+ # o | o | win
12
+ # ---------------
13
+ # x | x |
14
+ # ---------------
15
+ # | |
16
+ board[] = ['o', 'o', ' ', 'x', 'x', ' ', ' ', ' ', ' ']
17
+ ai.move(board: board)
18
+ ai.minimax.should == 2
19
+ end
20
+
21
+ it "blocks an opponnent's winning move when one is found" do
22
+ #situation: o to move
23
+ # x | x | block
24
+ # ---------------
25
+ # o | |
26
+ # ---------------
27
+ # o | |
28
+ board[] = ['x', 'x', ' ', 'o', ' ', ' ', 'o', ' ', ' ']
29
+ ai.move(board: board)
30
+ ai.minimax.should == 2
31
+ end
32
+
33
+ it "it prevents forking" do
34
+ #situation: o to move
35
+ # x | o |
36
+ # ---------------
37
+ # o | | x
38
+ # ---------------
39
+ # | x | best
40
+ board[] = ['x', 'o', ' ', 'o', ' ', 'x', ' ', 'x', ' ']
41
+ ai.move(board: board)
42
+ ai.minimax.should == 8
43
+ end
44
+ end
45
+
46
+ it "finds the best opening move on small games" do
47
+ board[] = Array.new(9, " ")
48
+ ai.move(board: board)
49
+ ai.minimax.should == 4
50
+ end
51
+
52
+ describe "#alpha_beta_swapped?" do
53
+ it "returns true when alpha >= beta" do
54
+ ai.alpha_beta_swapped?(500, 500).should == true
55
+ end
56
+
57
+ it "returns false when alpha < beta" do
58
+ ai.alpha_beta_swapped?(200, 800).should == false
59
+ end
60
+ end
61
+
62
+ describe "#max_ply_for" do
63
+ it "returns 7 when available moves > 16" do
64
+ ai.max_ply_for(17).should == 7
65
+ end
66
+
67
+ it "returns 9 when available moves <= 16 and > 10" do
68
+ ai.max_ply_for(12).should == 9
69
+ end
70
+
71
+ it "returns 11 when available moves <= 10" do
72
+ ai.max_ply_for(7).should == 11
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+
3
+ module TTT
4
+ describe AIMedium do
5
+ let(:ai) { AIMedium.new(side: "o") }
6
+ let(:board) { ThreeByThree.new }
7
+
8
+ describe "#minimax" do
9
+ it "blocks a potential fork win" do
10
+ #situation: x to move
11
+ # o | | x
12
+ # -----------------
13
+ # | o | block
14
+ # -----------------
15
+ # | | x
16
+ board[] = ['o', ' ', 'x', ' ', 'o', ' ', ' ', ' ', 'x']
17
+ ai.move(board: board)
18
+ ai.minimax.should == 5
19
+ end
20
+
21
+ it "takes a winning move when one is immediately avaialable" do
22
+ #situation: o to move
23
+ # o | o | win
24
+ # ---------------
25
+ # x | x |
26
+ # ---------------
27
+ # | |
28
+ board[] = ['o', 'o', ' ', 'x', 'x', ' ', ' ', ' ', ' ']
29
+ ai.move(board: board)
30
+ ai.minimax.should == 2
31
+ end
32
+
33
+ it "blocks an opponnent's winning move when one is found" do
34
+ #situation: o to move
35
+ # x | x | block
36
+ # ---------------
37
+ # o | |
38
+ # ---------------
39
+ # o | |
40
+ board[] = ['x', 'x', ' ', 'o', ' ', ' ', 'o', ' ', ' ']
41
+ ai.move(board: board)
42
+ ai.minimax.should == 2
43
+ end
44
+
45
+ it "returns 0 when it's a draw" do
46
+ board[] = ['x', 'o', 'o', 'o', 'x', 'x', 'o', 'x', 'o']
47
+ ai.move(board: board).should == 0
48
+ end
49
+ end
50
+
51
+ describe "#set_max_ply" do
52
+ it "returns 3 when number of available moves > 15" do
53
+ ai.set_max_ply(16).should == 3
54
+ end
55
+
56
+ it "returns 5 when number of available moves > 5 && < 15" do
57
+ ai.set_max_ply(14).should == 5
58
+ end
59
+
60
+ it "returns 7 when the number of available moves < 5" do
61
+ ai.set_max_ply(3).should == 7
62
+ end
63
+ end
64
+ end
65
+ end