tic_tac_toes 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,175 @@
1
+ require 'tic_tac_toes/board'
2
+ require 'tic_tac_toes/io_interface'
3
+ require 'tic_tac_toes/spec_helper'
4
+ require 'tic_tac_toes/strings'
5
+
6
+ describe TicTacToes::IOInterface do
7
+ let(:strings) { TicTacToes::Strings }
8
+ let(:io) { double("io", solicit_input: 0, display: true, display_red: true) }
9
+ let(:io_interface) { TicTacToes::IOInterface.new(io) }
10
+
11
+
12
+ describe '#make_move' do
13
+ it "displays a move solicitation" do
14
+ expect(io_interface).to receive(:move_solicitation)
15
+ io_interface.make_move("_board", "_players")
16
+ end
17
+
18
+ context 'when given not-integer-like input' do
19
+ let(:not_integer_like) { "string" }
20
+ let(:integer_like) { "100" }
21
+
22
+ it "displays a not an integer error" do
23
+ allow(io).to receive(:solicit_input).and_return(not_integer_like, integer_like)
24
+
25
+ expect(io_interface).to receive(:not_an_integer_error).once
26
+ io_interface.make_move("_board", "_players")
27
+ end
28
+
29
+ it "only returns a move (converted to integer) once it gets integer-like input" do
30
+ allow(io).to receive(:solicit_input).and_return(not_integer_like, integer_like)
31
+
32
+ expect(io_interface.make_move("_board", "_players")).to eq(100)
33
+ end
34
+ end
35
+ end
36
+
37
+
38
+ describe '#get_row_size' do
39
+ it "displays a row size solicitation" do
40
+ expect(io_interface).to receive(:row_size_solicitation)
41
+ io_interface.get_row_size
42
+ end
43
+
44
+ context 'when given not-integer-like input' do
45
+ let(:not_integer_like) { "string" }
46
+ let(:integer_like) { "100" }
47
+
48
+ it "displays a not an integer error" do
49
+ allow(io).to receive(:solicit_input).and_return(not_integer_like, integer_like)
50
+
51
+ expect(io_interface).to receive(:not_an_integer_error).once
52
+ io_interface.get_row_size
53
+ end
54
+
55
+ it "only returns a row size (converted to integer) once it gets integer-like input" do
56
+ allow(io).to receive(:solicit_input).and_return(not_integer_like, integer_like)
57
+
58
+ expect(io_interface.get_row_size).to eq(100)
59
+ end
60
+ end
61
+ end
62
+
63
+
64
+ describe '#get_token' do
65
+ let(:player) { double("player") }
66
+
67
+ it "displays a token solicitation with the name of the player whose token is to be set" do
68
+ expect(io_interface).to receive(:token_solicitation).with(player)
69
+ io_interface.get_token(player)
70
+ end
71
+
72
+ it "returns the user input" do
73
+ token = "X"
74
+ allow(io).to receive(:solicit_input) { token }
75
+
76
+ expect(io_interface.get_token(player)).to equal(token)
77
+ end
78
+ end
79
+
80
+
81
+ describe '#get_difficulty' do
82
+ let(:difficulty) { "HARD" }
83
+
84
+ it "displays a difficulty solicitation" do
85
+ allow(io).to receive(:solicit_input) { difficulty }
86
+
87
+ expect(io_interface).to receive(:difficulty_solicitation)
88
+ io_interface.get_difficulty
89
+ end
90
+
91
+ it "returns the user input (downcased and converted into a symbol)" do
92
+ allow(io).to receive(:solicit_input) { difficulty }
93
+
94
+ expect(io_interface.get_difficulty).to equal(:hard)
95
+ end
96
+ end
97
+
98
+
99
+ describe '#draw_board' do
100
+ it "displays a board string" do
101
+ board = TicTacToes::Board.new
102
+ board_string = strings.board(board)
103
+
104
+ expect(io).to receive(:display).with(board_string)
105
+ io_interface.draw_board(board)
106
+ end
107
+ end
108
+
109
+
110
+ describe '#invalid_row_size_error' do
111
+ it "displays a red invalid row size message" do
112
+ expect(io).to receive(:display_red).with(strings::INVALID_ROW_SIZE)
113
+ io_interface.invalid_row_size_error
114
+ end
115
+ end
116
+
117
+
118
+ describe '#invalid_token_error' do
119
+ it "displays a red invalid token message" do
120
+ expect(io).to receive(:display_red).with(strings::INVALID_TOKEN)
121
+ io_interface.invalid_token_error
122
+ end
123
+ end
124
+
125
+
126
+ describe '#invalid_difficulty_error' do
127
+ it "displays a red invalid difficulty message" do
128
+ expect(io).to receive(:display_red).with(strings::INVALID_DIFFICULTY)
129
+ io_interface.invalid_difficulty_error
130
+ end
131
+ end
132
+
133
+
134
+ describe '#invalid_move_error' do
135
+ it "displays a red invalid move message" do
136
+ expect(io).to receive(:display_red).with(strings::INVALID_MOVE)
137
+ io_interface.invalid_move_error
138
+ end
139
+ end
140
+
141
+
142
+ describe '#thinking_notification' do
143
+ it "displays a red thinking message" do
144
+ expect(io).to receive(:display_red).with(strings::THINKING)
145
+ io_interface.thinking_notification
146
+ end
147
+ end
148
+
149
+
150
+ describe '#game_over_notification' do
151
+ it "displays a game over message with the name of the winner" do
152
+ winner = :X
153
+ game_over_message = strings.game_over_notification(winner)
154
+
155
+ expect(io).to receive(:display).with(game_over_message)
156
+ io_interface.game_over_notification(winner)
157
+ end
158
+ end
159
+
160
+
161
+ describe '#red' do
162
+ it "calls its IO's red method with the passed message" do
163
+ expect(io).to receive(:red).with("message")
164
+ io_interface.red("message")
165
+ end
166
+ end
167
+
168
+
169
+ describe '#blue' do
170
+ it "calls its IO's blue method with the passed message" do
171
+ expect(io).to receive(:blue).with("message")
172
+ io_interface.blue("message")
173
+ end
174
+ end
175
+ end
@@ -0,0 +1,22 @@
1
+ require 'tic_tac_toes/medium_ai'
2
+ require 'tic_tac_toes/spec_helper'
3
+
4
+ describe TicTacToes::MediumAI do
5
+ let(:ai) { TicTacToes::MediumAI }
6
+ let(:x) { TicTacToes::Player.new("decider", "x", false, "interface") }
7
+ let(:o) { TicTacToes::Player.new(ai, "o", true, "interface") }
8
+ let(:players) { [x, o] }
9
+
10
+ describe '#make_move' do
11
+ it "makes a valid move (based on either EasyAI or HardAI)" do
12
+ structure = [ o, o, x,
13
+ nil, x, nil,
14
+ nil, x, nil]
15
+ board = generate_board(structure)
16
+ valid_moves = [3, 5, 6, 8]
17
+
18
+ move = ai.make_move(board, players)
19
+ expect(valid_moves).to include(move)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,34 @@
1
+ require 'command_line/io'
2
+ require 'tic_tac_toes/io_interface'
3
+ require 'tic_tac_toes/medium_ai'
4
+ require 'tic_tac_toes/player_factory'
5
+ require 'tic_tac_toes/spec_helper'
6
+
7
+ describe TicTacToes::PlayerFactory do
8
+ let(:io) { CommandLine::IO }
9
+ let(:io_interface) { TicTacToes::IOInterface.new(io) }
10
+ let(:player_factory) { TicTacToes::PlayerFactory.new(io_interface) }
11
+
12
+
13
+ describe '#generate_human_player' do
14
+ it "returns a player with the correct token and decider" do
15
+ token = "X"
16
+
17
+ human_player = player_factory.generate_human_player(token)
18
+ expect(human_player.decider).to be_a TicTacToes::IOInterface
19
+ expect(human_player.token).to eq(token)
20
+ end
21
+ end
22
+
23
+
24
+ describe '#generate_computer_player' do
25
+ it "returns a player with the correct token and decider when given a valid difficulty" do
26
+ token = "O"
27
+ difficulty = :medium
28
+
29
+ computer_player = player_factory.generate_computer_player(token, difficulty)
30
+ expect(computer_player.decider).to eql(TicTacToes::MediumAI)
31
+ expect(computer_player.token).to eq(token)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,34 @@
1
+ require 'tic_tac_toes/board'
2
+ require 'tic_tac_toes/io_interface'
3
+ require 'tic_tac_toes/player'
4
+ require 'tic_tac_toes/spec_helper'
5
+
6
+ describe TicTacToes::Player do
7
+ let(:board) { TicTacToes::Board.new(row_size: 3) }
8
+ let(:io_interface) { double("io_interface", invalid_move_error: true) }
9
+ let(:players) { double("players") }
10
+
11
+ describe '#make_move' do
12
+ let(:decider) { double("decider") }
13
+ let(:token) { "X" }
14
+ let(:needs_to_think) { false }
15
+
16
+ let(:player) { TicTacToes::Player.new(decider, token, needs_to_think, io_interface) }
17
+
18
+ it "only returns a move once it receives a valid move" do
19
+ invalid_space, valid_space = 9, 0
20
+ allow(decider).to receive(:make_move).and_return(invalid_space, valid_space)
21
+
22
+ player.make_move(board, players)
23
+ expect(board.space(valid_space)).to eq(player)
24
+ end
25
+
26
+ it "displays an invalid move error when given an invalid move" do
27
+ invalid_space, valid_space = 9, 0
28
+ allow(decider).to receive(:make_move).and_return(invalid_space, valid_space)
29
+
30
+ expect(io_interface).to receive(:invalid_move_error)
31
+ player.make_move(board, players)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,159 @@
1
+ require 'tic_tac_toes/spec_helper'
2
+ require 'tic_tac_toes/rules'
3
+
4
+ describe TicTacToes::Rules do
5
+ let(:rules) { TicTacToes::Rules }
6
+ let(:x) { double("human player", token: "x") }
7
+ let(:o) { double("computer player", token: "o") }
8
+ let(:players) { [x, o] }
9
+
10
+ describe '#row_size_valid?' do
11
+ it "returns false if the row size is outside the range 2 to 10" do
12
+ invalid_row_size = 11
13
+
14
+ expect(rules.row_size_valid?(invalid_row_size)).to be false
15
+ end
16
+
17
+ it "returns true if the row size is in the range 2 to 10" do
18
+ valid_row_size = 10
19
+
20
+ expect(rules.row_size_valid?(valid_row_size)).to be true
21
+ end
22
+ end
23
+
24
+
25
+ describe '#token_valid?' do
26
+ it "returns false if the token is not a single character" do
27
+ long_token, taken_tokens = :long, []
28
+
29
+ expect(rules.token_valid?(long_token, taken_tokens)).to be false
30
+ end
31
+
32
+ it "returns false if the token is already taken" do
33
+ taken_token, taken_tokens = :X, [:X]
34
+
35
+ expect(rules.token_valid?(taken_token, taken_tokens)).to be false
36
+ end
37
+
38
+ it "returns true if the token is a single character and untaken" do
39
+ valid_token, taken_tokens = :O, [:X]
40
+
41
+ expect(rules.token_valid?(valid_token, taken_tokens)).to be true
42
+ end
43
+ end
44
+
45
+
46
+ describe '#difficulty_valid?' do
47
+ it "returns false if the difficulty is not a key in AI_DIFFICULTIES" do
48
+ invalid_difficulty = :invalid
49
+
50
+ expect(rules.difficulty_valid?(invalid_difficulty)).to be false
51
+ end
52
+
53
+ it "returns true if the difficulty is a key in AI_DIFFICULTIES" do
54
+ valid_difficulty = :medium
55
+
56
+ expect(rules.difficulty_valid?(valid_difficulty)).to be true
57
+ end
58
+ end
59
+
60
+
61
+ describe '#game_over?' do
62
+ it "returns false if there is not yet a winner and the board is not full" do
63
+ structure = [x, x, o,
64
+ o, o, nil,
65
+ x, o, x]
66
+ board = generate_board(structure)
67
+
68
+ expect(rules.game_over?(board, players)).to be false
69
+ end
70
+
71
+ it "returns true if any player has won" do
72
+ structure = [ x, nil, nil,
73
+ nil, x, nil,
74
+ nil, nil, x]
75
+ board = generate_board(structure)
76
+
77
+ expect(rules.game_over?(board, players)).to be true
78
+ end
79
+
80
+ it "returns true if the board is full" do
81
+ structure = [x, x, o,
82
+ o, o, x,
83
+ x, o, x]
84
+ board = generate_board(structure)
85
+
86
+ expect(rules.game_over?(board, players)).to be true
87
+ end
88
+ end
89
+
90
+
91
+ describe '#determine_winner' do
92
+ it "returns the winning token when there is a winner" do
93
+ structure = [ o, nil, nil,
94
+ nil, o, nil,
95
+ nil, nil, o]
96
+ board = generate_board(structure)
97
+ winning_token = "o"
98
+
99
+ expect(rules.determine_winner(board, players)).to eql(winning_token)
100
+ end
101
+
102
+ it "returns nil if there is not a winner" do
103
+ structure = [x, x, o,
104
+ o, o, nil,
105
+ x, o, x]
106
+ board = generate_board(structure)
107
+
108
+ expect(rules.determine_winner(board, players)).to be_nil
109
+ end
110
+ end
111
+
112
+
113
+ describe '#win?' do
114
+ it "returns false if the given token has not won" do
115
+ structure = [x, x, o,
116
+ o, o, nil,
117
+ x, o, x]
118
+ board = generate_board(structure)
119
+
120
+ expect(rules.win?(board, x)).to be false
121
+ end
122
+
123
+ it "returns true if the given token has achieved a back diagonal win" do
124
+ structure = [ x, nil, nil,
125
+ nil, x, nil,
126
+ nil, nil, x]
127
+ board = generate_board(structure)
128
+
129
+ expect(rules.win?(board, x)).to be true
130
+ end
131
+
132
+ it "returns true if the given token has achieved a front diagonal win" do
133
+ structure = [nil, nil, x,
134
+ nil, x, nil,
135
+ x, nil, nil]
136
+ board = generate_board(structure)
137
+
138
+ expect(rules.win?(board, x)).to be true
139
+ end
140
+
141
+ it "returns true if the given token has achieved a horizonatal win" do
142
+ structure = [nil, nil, nil,
143
+ x, x, x,
144
+ nil, nil, nil]
145
+ board = generate_board(structure)
146
+
147
+ expect(rules.win?(board, x)).to be true
148
+ end
149
+
150
+ it "returns true if the given token has achieved a vertical win" do
151
+ structure = [nil, x, nil,
152
+ nil, x, nil,
153
+ nil, x, nil]
154
+ board = generate_board(structure)
155
+
156
+ expect(rules.win?(board, x)).to be true
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,13 @@
1
+ require 'tic_tac_toes/board'
2
+
3
+ def generate_board(structure)
4
+ board_size = structure.count
5
+ row_size = Math.sqrt(board_size)
6
+ board = TicTacToes::Board.new(row_size: row_size)
7
+
8
+ structure.each_with_index do |player, index|
9
+ board.place(player, index)
10
+ end
11
+
12
+ board
13
+ end
@@ -0,0 +1,50 @@
1
+ require 'tic_tac_toes/board'
2
+ require 'tic_tac_toes/rules'
3
+ require 'tic_tac_toes/spec_helper'
4
+ require 'tic_tac_toes/strings'
5
+
6
+ describe TicTacToes::Strings do
7
+ let(:strings) { TicTacToes::Strings }
8
+
9
+ describe '#invalid_row_size' do
10
+ it "returns a string containing the minimum and maximum allowed row sizes" do
11
+ smallest_row_size = TicTacToes::Rules::ROW_SIZE_RANGE.min.to_s
12
+ largest_row_size = TicTacToes::Rules::ROW_SIZE_RANGE.max.to_s
13
+
14
+ expect(strings::INVALID_ROW_SIZE).to include(smallest_row_size, largest_row_size)
15
+ end
16
+ end
17
+
18
+ describe '#token_solicitation' do
19
+ it "returns a string containing the name of the player whose token is being picked" do
20
+ player = "human"
21
+ expect(strings.token_solicitation(player)).to include(player)
22
+ end
23
+ end
24
+
25
+ describe '#game_over_notification' do
26
+ it "returns a string containing the name of the winner" do
27
+ winner = "X"
28
+ expect(strings.game_over_notification(winner)).to include(winner)
29
+ end
30
+ end
31
+
32
+ describe '#board' do
33
+ it "returns a board string" do
34
+ board = TicTacToes::Board.new(row_size: 3)
35
+
36
+ leading_spaces_regexp = /^[[:blank:]]+/
37
+ board_string = <<-END.gsub(leading_spaces_regexp, '')
38
+
39
+ [0]|[1]|[2]
40
+ -----------
41
+ [3]|[4]|[5]
42
+ -----------
43
+ [6]|[7]|[8]
44
+
45
+ END
46
+
47
+ expect(strings.board(board)).to eql(board_string)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'tic_tac_toes/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "tic_tac_toes"
8
+ spec.version = TicTacToes::VERSION
9
+ spec.authors = ["Ben Spatafora"]
10
+ spec.email = ["benjamin.spatafora@gmail.com"]
11
+ spec.summary = %q{The game Tic-tac-toe, featuring an unbeatable AI}
12
+ spec.homepage = "http://github.com/bspatafora/tic_tac_toes"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = ["tic_tac_toes"]
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.6"
21
+ spec.add_development_dependency "rake"
22
+ spec.add_development_dependency "rspec"
23
+ spec.add_dependency "pg"
24
+ end
metadata ADDED
@@ -0,0 +1,151 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tic_tac_toes
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ben Spatafora
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-06-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pg
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description:
70
+ email:
71
+ - benjamin.spatafora@gmail.com
72
+ executables:
73
+ - tic_tac_toes
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - Gemfile
79
+ - LICENSE.txt
80
+ - README.md
81
+ - Rakefile
82
+ - bin/tic_tac_toes
83
+ - lib/command_line/io.rb
84
+ - lib/command_line/menu.rb
85
+ - lib/command_line/runner.rb
86
+ - lib/database/pg_wrapper.rb
87
+ - lib/tic_tac_toes/board.rb
88
+ - lib/tic_tac_toes/easy_ai.rb
89
+ - lib/tic_tac_toes/hard_ai.rb
90
+ - lib/tic_tac_toes/history.rb
91
+ - lib/tic_tac_toes/io_interface.rb
92
+ - lib/tic_tac_toes/medium_ai.rb
93
+ - lib/tic_tac_toes/player.rb
94
+ - lib/tic_tac_toes/player_factory.rb
95
+ - lib/tic_tac_toes/rules.rb
96
+ - lib/tic_tac_toes/strings.rb
97
+ - lib/tic_tac_toes/version.rb
98
+ - spec/command_line/menu_spec.rb
99
+ - spec/command_line/runner_spec.rb
100
+ - spec/database/pg_wrapper_spec.rb
101
+ - spec/tic_tac_toes/board_spec.rb
102
+ - spec/tic_tac_toes/easy_ai_spec.rb
103
+ - spec/tic_tac_toes/hard_ai_spec.rb
104
+ - spec/tic_tac_toes/history_spec.rb
105
+ - spec/tic_tac_toes/io_interface_spec.rb
106
+ - spec/tic_tac_toes/medium_ai_spec.rb
107
+ - spec/tic_tac_toes/player_factory_spec.rb
108
+ - spec/tic_tac_toes/player_spec.rb
109
+ - spec/tic_tac_toes/rules_spec.rb
110
+ - spec/tic_tac_toes/spec_helper.rb
111
+ - spec/tic_tac_toes/strings_spec.rb
112
+ - tic_tac_toes.gemspec
113
+ homepage: http://github.com/bspatafora/tic_tac_toes
114
+ licenses:
115
+ - MIT
116
+ metadata: {}
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ required_rubygems_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ requirements: []
132
+ rubyforge_project:
133
+ rubygems_version: 2.2.2
134
+ signing_key:
135
+ specification_version: 4
136
+ summary: The game Tic-tac-toe, featuring an unbeatable AI
137
+ test_files:
138
+ - spec/command_line/menu_spec.rb
139
+ - spec/command_line/runner_spec.rb
140
+ - spec/database/pg_wrapper_spec.rb
141
+ - spec/tic_tac_toes/board_spec.rb
142
+ - spec/tic_tac_toes/easy_ai_spec.rb
143
+ - spec/tic_tac_toes/hard_ai_spec.rb
144
+ - spec/tic_tac_toes/history_spec.rb
145
+ - spec/tic_tac_toes/io_interface_spec.rb
146
+ - spec/tic_tac_toes/medium_ai_spec.rb
147
+ - spec/tic_tac_toes/player_factory_spec.rb
148
+ - spec/tic_tac_toes/player_spec.rb
149
+ - spec/tic_tac_toes/rules_spec.rb
150
+ - spec/tic_tac_toes/spec_helper.rb
151
+ - spec/tic_tac_toes/strings_spec.rb