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