bashrw_ttt 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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