rttt 0.1

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