rttt 0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/ai.rb +36 -0
- data/lib/board.rb +21 -0
- data/lib/player_manager.rb +36 -0
- data/lib/state.rb +71 -0
- data/spec/ai_spec.rb +86 -0
- data/spec/board_spec.rb +46 -0
- data/spec/player_manager_spec.rb +20 -0
- data/spec/state_spec.rb +152 -0
- metadata +69 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6b315e2219bf31c09ba048bb4146d5e94fa4ebe9
|
4
|
+
data.tar.gz: e37ab3d75a80f405f3b8cce6eb51dcb2b690861e
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b332b109191dd60d1c4bf0f174784618a7566b69401bc000af3cca4275a35aacdf20a14564b5f48d8d16232f795abf291d7c13ac256131bfb4596c0a2f2ae069
|
7
|
+
data.tar.gz: c6401dc23e7a92d2e757bb1b22b76498bf868b4b37cc81af6152798a259f8a6ccf5f561ec9efd463b6a1cc5c0fdb4f2955f12aabdb4a11f22d4f3d3b328f711e
|
data/lib/ai.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'player_manager'
|
2
|
+
require 'state'
|
3
|
+
|
4
|
+
class Ai
|
5
|
+
attr_reader :mark, :choice
|
6
|
+
|
7
|
+
def initialize(mark, manager)
|
8
|
+
@mark = mark
|
9
|
+
@state = State.new(manager)
|
10
|
+
@manager = manager
|
11
|
+
end
|
12
|
+
|
13
|
+
def score(board, mark)
|
14
|
+
if @state.winner?(board, mark) || @state.winner?(board, @manager.other_mark(mark))
|
15
|
+
-10
|
16
|
+
else
|
17
|
+
0
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def score_moves(board, mark, depth, scores)
|
22
|
+
return score(board, mark) if @state.terminal?(board) || (board.size == 16 && depth == 2)
|
23
|
+
@state.empty_indices(board).each do |move|
|
24
|
+
board[move] = mark
|
25
|
+
scores[move] = -1 * score_moves(board, @manager.other_mark(mark), depth + 1, Hash.new)
|
26
|
+
board[move] = "-"
|
27
|
+
end
|
28
|
+
best_score = scores.values.max
|
29
|
+
depth == 0 ? scores : best_score
|
30
|
+
end
|
31
|
+
|
32
|
+
def smart_move(board)
|
33
|
+
@choice = score_moves(board, @mark, 0, Hash.new).max_by{ |k, v| v }[0]
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/lib/board.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
class Board
|
2
|
+
attr_reader :board
|
3
|
+
|
4
|
+
def initialize(n)
|
5
|
+
@board = Array.new(n*n, "-")
|
6
|
+
end
|
7
|
+
|
8
|
+
def place(mark, loc)
|
9
|
+
@board[loc] = mark if valid?(loc)
|
10
|
+
@board
|
11
|
+
end
|
12
|
+
|
13
|
+
def valid?(loc)
|
14
|
+
@board[loc] == "-" && loc > -1 && loc < @board.length
|
15
|
+
end
|
16
|
+
|
17
|
+
def value_board
|
18
|
+
@board.clone
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'ai.rb'
|
2
|
+
|
3
|
+
class Player_manager
|
4
|
+
attr_reader :first, :second
|
5
|
+
|
6
|
+
def initialize(first, ai_one, second, ai_two)
|
7
|
+
@first = first
|
8
|
+
@second = second
|
9
|
+
|
10
|
+
@players = {}
|
11
|
+
if ai_one == true
|
12
|
+
@players[@first] = Ai.new(@first, self)
|
13
|
+
else
|
14
|
+
@players[@first] = "human"
|
15
|
+
end
|
16
|
+
|
17
|
+
if ai_two == true
|
18
|
+
@players[@second] = Ai.new(@second, self)
|
19
|
+
else
|
20
|
+
@players[@second] = "human"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def other_mark(mark)
|
25
|
+
if mark == @first
|
26
|
+
@second
|
27
|
+
else
|
28
|
+
@first
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_player(mark)
|
33
|
+
@players[mark]
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
data/lib/state.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
class State
|
2
|
+
def initialize(manager)
|
3
|
+
@manager = manager
|
4
|
+
end
|
5
|
+
|
6
|
+
def empty_indices(board)
|
7
|
+
indices = []
|
8
|
+
for i in 0..board.length - 1
|
9
|
+
indices.push(i) if board[i] == "-"
|
10
|
+
end
|
11
|
+
indices
|
12
|
+
end
|
13
|
+
|
14
|
+
def full?(board)
|
15
|
+
empty_indices(board) == []
|
16
|
+
end
|
17
|
+
|
18
|
+
def tie?(board)
|
19
|
+
full?(board) && !winner?(board, @manager.first) && !winner?(board, @manager.second)
|
20
|
+
end
|
21
|
+
|
22
|
+
def horizontal?(board, mark)
|
23
|
+
count = 0
|
24
|
+
(0..board.size - 1).each do |pos|
|
25
|
+
count = 0 if pos % Math.sqrt(board.size) == 0
|
26
|
+
if board[pos] == mark
|
27
|
+
count += 1
|
28
|
+
return true if count == Math.sqrt(board.size)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
return false
|
32
|
+
end
|
33
|
+
|
34
|
+
def vertical?(board, mark)
|
35
|
+
(0..Math.sqrt(board.size) - 1).each do |col|
|
36
|
+
count = 0
|
37
|
+
for row in 0..board.size - 1
|
38
|
+
count += 1 if (row % Math.sqrt(board.size)) == 0 && board[col+row] == mark
|
39
|
+
end
|
40
|
+
return true if count == Math.sqrt(board.size)
|
41
|
+
end
|
42
|
+
return false
|
43
|
+
end
|
44
|
+
|
45
|
+
def diagonal_major?(board, mark)
|
46
|
+
count = 0
|
47
|
+
(0..board.size - 1).each do |pos|
|
48
|
+
count += 1 if (pos % (Math.sqrt(board.size) + 1)) == 0 && board[pos] == mark
|
49
|
+
return true if count == Math.sqrt(board.size)
|
50
|
+
end
|
51
|
+
return false
|
52
|
+
end
|
53
|
+
|
54
|
+
def diagonal_minor?(board, mark)
|
55
|
+
count = 0
|
56
|
+
(Integer(Math.sqrt(board.size) - 1)..board.size - (Math.sqrt(board.size))).each do |pos|
|
57
|
+
count += 1 if (pos % (Math.sqrt(board.size) - 1)) == 0 && board[pos] == mark
|
58
|
+
return true if count == Math.sqrt(board.size)
|
59
|
+
end
|
60
|
+
return false
|
61
|
+
end
|
62
|
+
|
63
|
+
def winner?(board, mark)
|
64
|
+
horizontal?(board, mark) || vertical?(board, mark) || diagonal_major?(board, mark) || diagonal_minor?(board, mark)
|
65
|
+
end
|
66
|
+
|
67
|
+
def terminal?(board)
|
68
|
+
winner?(board, @manager.first) || winner?(board, @manager.second) || tie?(board)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
data/spec/ai_spec.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'state'
|
2
|
+
require 'ai'
|
3
|
+
require 'player_manager'
|
4
|
+
|
5
|
+
describe Ai do
|
6
|
+
let(:m) {Player_manager.new("x", false, "o", true)}
|
7
|
+
let(:a) {Ai.new("o", m)}
|
8
|
+
|
9
|
+
it "scores all games -10" do
|
10
|
+
board = ["o", "o", "o","-","-","-","-","-","-"]
|
11
|
+
a.score(board, "o").should eq(-10)
|
12
|
+
|
13
|
+
board = ["x", "x", "x","-","-","-","-","-","-"]
|
14
|
+
a.score(board, "o").should eq(-10)
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
it "doesn't score a draw a -10" do
|
19
|
+
board = ["x", "o", "x",
|
20
|
+
"o", "-", "-",
|
21
|
+
"x", "-", "o"]
|
22
|
+
a.score(board, "o").should eq(0)
|
23
|
+
|
24
|
+
board = ["x", "o", "x",
|
25
|
+
"o", "-", "-",
|
26
|
+
"x", "-", "o"]
|
27
|
+
a.score(board, "x").should eq(0)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "scores a draw 0" do
|
31
|
+
board = ["x", "o", "x","x","o","o","o","x","x"]
|
32
|
+
a.score(board, "o").should eq(0)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "if game is over, return the score for that branch" do
|
36
|
+
board = ["o", "o", "o","-","-","-","-","-","-"]
|
37
|
+
a.score(board, "o").should eq(-10)
|
38
|
+
board = ["x", "x", "x","-","-","-","-","-","-"]
|
39
|
+
a.score(board, "o").should eq(-10)
|
40
|
+
board = ["x", "o", "x","x","o","o","o","x","x"]
|
41
|
+
a.score(board, "o").should eq(0)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should take the correct move" do
|
45
|
+
board = ["x", "x", "-","-","o","-","-","-","-"]
|
46
|
+
a.smart_move(board)
|
47
|
+
a.choice.should eq(2)
|
48
|
+
|
49
|
+
board = ["o", "o", "x","-","-","-","x","-","-"]
|
50
|
+
a.smart_move(board)
|
51
|
+
a.choice.should eq(4)
|
52
|
+
|
53
|
+
board = ["-", "-", "o","-","o","x","x","-","x"]
|
54
|
+
a.smart_move(board)
|
55
|
+
a.choice.should eq(7)
|
56
|
+
|
57
|
+
board = ["x", "x", "o","-","o","-","x","-","-"]
|
58
|
+
a.smart_move(board)
|
59
|
+
a.choice.should eq(3)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should block a fork" do
|
63
|
+
board = ["-", "-", "-",
|
64
|
+
"-", "x", "-",
|
65
|
+
"x", "-", "o"]
|
66
|
+
a.smart_move(board)
|
67
|
+
a.choice.should eq(2)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "another fork" do
|
71
|
+
board = ["-", "-", "x",
|
72
|
+
"-", "x", "-",
|
73
|
+
"o", "-", "-"]
|
74
|
+
a.smart_move(board)
|
75
|
+
[0, 8].should include(a.choice)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "another fork" do
|
79
|
+
board = ["x", "o", "x",
|
80
|
+
"-", "-", "-",
|
81
|
+
"-", "-", "-"]
|
82
|
+
a.smart_move(board)
|
83
|
+
[4, 6, 8].should include(a.choice)
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
data/spec/board_spec.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'board'
|
2
|
+
|
3
|
+
describe Board do
|
4
|
+
let(:b) {Board.new(3)}
|
5
|
+
|
6
|
+
it "initializes an empty board with blank (-) spaces" do
|
7
|
+
b.board.should eq(["-", "-","-","-","-","-","-","-","-"])
|
8
|
+
end
|
9
|
+
|
10
|
+
it "can take an piece" do
|
11
|
+
b.place("x", 0).should eq(["x", "-","-","-","-","-","-","-","-"])
|
12
|
+
end
|
13
|
+
|
14
|
+
it "index out of bounds (lower) aren't valid" do
|
15
|
+
b.valid?(-1).should eq(false)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "index out of bounds (greater) aren't valid" do
|
19
|
+
b.valid?(9).should eq(false)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "Taken positions are not valid" do
|
23
|
+
b.place("x", 0)
|
24
|
+
b.valid?(0).should eq(false)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "won't place a piece in an invalid spot" do
|
28
|
+
b.place("x", -1).should eq(["-", "-","-","-","-","-","-","-","-"])
|
29
|
+
end
|
30
|
+
|
31
|
+
it "won't place a piece in an invalid spot" do
|
32
|
+
b.place("x", 11).should eq(["-", "-","-","-","-","-","-","-","-"])
|
33
|
+
end
|
34
|
+
|
35
|
+
it "won't place a piece in an taken position" do
|
36
|
+
b.place("x", 0)
|
37
|
+
b.place("o", 0).should eq(["x", "-","-","-","-","-","-","-","-"])
|
38
|
+
end
|
39
|
+
|
40
|
+
it "passed by values not by reference" do
|
41
|
+
board = b.value_board
|
42
|
+
board[8] = "x"
|
43
|
+
b.board.should eq(["-", "-","-","-","-","-","-","-","-"])
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'player_manager'
|
2
|
+
|
3
|
+
describe Player_manager do
|
4
|
+
let(:p) {Player_manager.new("x", false, "o", true)}
|
5
|
+
|
6
|
+
it "reports opposing player" do
|
7
|
+
p.other_mark("x").should eq("o")
|
8
|
+
end
|
9
|
+
|
10
|
+
it "reports opposing player" do
|
11
|
+
p.other_mark("x").should eq("o")
|
12
|
+
end
|
13
|
+
|
14
|
+
it "knows if player is ai or not" do
|
15
|
+
p.get_player("x").should eq("human")
|
16
|
+
player = p.get_player("o")
|
17
|
+
player.class.should eq(Ai)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
data/spec/state_spec.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
require 'state'
|
2
|
+
require 'board'
|
3
|
+
require 'player_manager'
|
4
|
+
|
5
|
+
describe State do
|
6
|
+
let(:b) {Board.new(3)}
|
7
|
+
let(:m) {Player_manager.new("x", false, "o", true)}
|
8
|
+
let(:s) {State.new(m)}
|
9
|
+
|
10
|
+
it "returns indices of empty positions on board" do
|
11
|
+
s.empty_indices(b.board).should eq([0, 1, 2, 3, 4, 5, 6, 7, 8])
|
12
|
+
end
|
13
|
+
|
14
|
+
it "won't return filled positions" do
|
15
|
+
s.empty_indices(b.place("x", 0)).should eq([1, 2, 3, 4, 5, 6, 7, 8])
|
16
|
+
end
|
17
|
+
|
18
|
+
it "reports if the board is full" do
|
19
|
+
board = ["x", "o","x","o","o","o","x","o","x"]
|
20
|
+
s.full?(board).should eq(true)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "won't if it isn't" do
|
24
|
+
board = ["x", "o","x","o","x","o","-","-","-"]
|
25
|
+
s.full?(board).should eq(false)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "reports status in horizontal row" do
|
29
|
+
board = ["x", "x", "x","-","-","-","-","-","-"]
|
30
|
+
s.horizontal?(board, "x").should eq(true)
|
31
|
+
board = ["-","-","-","-","-","-","x", "x", "x"]
|
32
|
+
s.horizontal?(board, "x").should eq(true)
|
33
|
+
board = ["-","-","-","-","-","-","o", "x", "x"]
|
34
|
+
s.horizontal?(board, "x").should eq(false)
|
35
|
+
board = ["-", "-", "-",
|
36
|
+
"o", "x", "x",
|
37
|
+
"-", "-", "-"]
|
38
|
+
s.horizontal?(board, "x").should eq(false)
|
39
|
+
board = ["o", "-", "o",
|
40
|
+
"-", "x", "-",
|
41
|
+
"x", "-", "x"]
|
42
|
+
s.horizontal?(board, "x").should eq(false)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "reports status in vertical row" do
|
46
|
+
board = ["x", "-", "-","x","-","-","x","-","-"]
|
47
|
+
s.vertical?(board, "x").should eq(true)
|
48
|
+
board = ["-", "-", "x","-","-","x","-","-","x"]
|
49
|
+
s.vertical?(board, "x").should eq(true)
|
50
|
+
board = ["x", "-", "-",
|
51
|
+
"-", "x", "-",
|
52
|
+
"x", "-", "-"]
|
53
|
+
s.vertical?(board, "x").should eq(false)
|
54
|
+
board = ["o", "-", "o",
|
55
|
+
"x", "o", "-",
|
56
|
+
"x", "-", "x"]
|
57
|
+
s.vertical?(board, "x").should eq(false)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "reports status in major diagonal row" do
|
61
|
+
board = ["x", "-", "-",
|
62
|
+
"-","x","-",
|
63
|
+
"-","-","x"]
|
64
|
+
s.diagonal_major?(board, "x").should eq(true)
|
65
|
+
board = ["-", "-", "x",
|
66
|
+
"-", "-", "x",
|
67
|
+
"-" ,"-", "x"]
|
68
|
+
s.diagonal_major?(board, "x").should eq(false)
|
69
|
+
board = ["o", "-", "o",
|
70
|
+
"-", "x", "-",
|
71
|
+
"x", "-", "x"]
|
72
|
+
s.diagonal_major?(board, "x").should eq(false)
|
73
|
+
|
74
|
+
board = ["x", "-", "-",
|
75
|
+
"-","x","-",
|
76
|
+
"-","-","-"]
|
77
|
+
s.diagonal_major?(board, "x").should eq(false)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "reports status in minor diagonal row" do
|
81
|
+
board = ["-", "-", "x",
|
82
|
+
"-", "x", "-",
|
83
|
+
"x", "-", "-"]
|
84
|
+
s.diagonal_minor?(board, "x").should eq(true)
|
85
|
+
board = ["-", "-", "x",
|
86
|
+
"-", "-", "x",
|
87
|
+
"-", "-", "x"]
|
88
|
+
s.diagonal_minor?(board, "x").should eq(false)
|
89
|
+
board = ["o", "-", "o",
|
90
|
+
"-", "x", "-",
|
91
|
+
"x", "-", "x"]
|
92
|
+
s.diagonal_minor?(board, "x").should eq(false)
|
93
|
+
board = ["-", "-", "-",
|
94
|
+
"-", "x", "-",
|
95
|
+
"x", "-", "o"]
|
96
|
+
s.diagonal_minor?(board, "x").should eq(false)
|
97
|
+
end
|
98
|
+
|
99
|
+
it "reports winner" do
|
100
|
+
board = ["x", "-", "-","-","x","-","-","-","x"]
|
101
|
+
s.winner?(board, "x").should eq(true)
|
102
|
+
board = ["x", "-", "-","x","-","-","x","-","-"]
|
103
|
+
s.winner?(board, "x").should eq(true)
|
104
|
+
board = ["x", "x", "x","-","-","-","-","-","-"]
|
105
|
+
s.winner?(board, "x").should eq(true)
|
106
|
+
board = ["o", "-", "o",
|
107
|
+
"-", "x", "-",
|
108
|
+
"x", "-", "x"]
|
109
|
+
s.winner?(board, "x").should eq(false)
|
110
|
+
end
|
111
|
+
|
112
|
+
it "reports end of game (win or draw)" do
|
113
|
+
board = ["x", "-", "-","-","x","-","-","-","x"]
|
114
|
+
s.terminal?(board).should eq(true)
|
115
|
+
board = ["x", "o", "x","x","o","x","o","x","o"]
|
116
|
+
s.terminal?(board).should eq(true)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "reports if the board is a tie game" do
|
120
|
+
board = ["x", "o","x","o","o","o","x","o","x"]
|
121
|
+
s.tie?(board).should eq(false)
|
122
|
+
end
|
123
|
+
|
124
|
+
it "hmmm?" do
|
125
|
+
board = ["x", "x", "o","x","o","o","x","-","-"]
|
126
|
+
s.winner?(board, "x").should eq(true)
|
127
|
+
end
|
128
|
+
|
129
|
+
it "work the same 4x4" do
|
130
|
+
board = ["x", "x", "x","x",
|
131
|
+
"-", "-", "-", "-",
|
132
|
+
"-", "-", "-", "-",
|
133
|
+
"-", "-", "-", "-"]
|
134
|
+
s.horizontal?(board, "x").should eq(true)
|
135
|
+
board = ["x", "-", "-","-",
|
136
|
+
"x", "-", "-", "-",
|
137
|
+
"x", "-", "-", "-",
|
138
|
+
"x", "-", "-", "-"]
|
139
|
+
s.vertical?(board, "x").should eq(true)
|
140
|
+
board = ["x", "-", "-","-",
|
141
|
+
"-", "x", "-", "-",
|
142
|
+
"-", "-", "x", "-",
|
143
|
+
"-", "-", "-", "x"]
|
144
|
+
s.diagonal_major?(board, "x").should eq(true)
|
145
|
+
board = ["-", "-", "-","x",
|
146
|
+
"-", "-", "x", "-",
|
147
|
+
"-", "x", "-", "-",
|
148
|
+
"x", "-", "-", "-"]
|
149
|
+
s.diagonal_minor?(board, "x").should eq(true)
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rttt
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andrew Spalding
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-08-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
description: Unbeatable tic tac toe to be plugged into a user interface.
|
28
|
+
email: aspalding@8thlight.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- lib/ai.rb
|
34
|
+
- lib/state.rb
|
35
|
+
- lib/board.rb
|
36
|
+
- lib/player_manager.rb
|
37
|
+
- spec/ai_spec.rb
|
38
|
+
- spec/state_spec.rb
|
39
|
+
- spec/board_spec.rb
|
40
|
+
- spec/player_manager_spec.rb
|
41
|
+
homepage: http://rubygems.org/gems/rttt
|
42
|
+
licenses:
|
43
|
+
- ''
|
44
|
+
metadata: {}
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - '>='
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
requirements: []
|
60
|
+
rubyforge_project:
|
61
|
+
rubygems_version: 2.0.14
|
62
|
+
signing_key:
|
63
|
+
specification_version: 4
|
64
|
+
summary: Unbeatable tic tac toe.
|
65
|
+
test_files:
|
66
|
+
- spec/ai_spec.rb
|
67
|
+
- spec/state_spec.rb
|
68
|
+
- spec/board_spec.rb
|
69
|
+
- spec/player_manager_spec.rb
|