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.
@@ -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