jdl_tictactoe 0.0.1 → 0.0.2
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.
- data/lib/jdl_tictactoe/board.rb +95 -0
- data/lib/jdl_tictactoe/game.rb +39 -0
- data/lib/jdl_tictactoe/player.rb +57 -0
- data/lib/jdl_tictactoe.rb +5 -3
- metadata +4 -1
@@ -0,0 +1,95 @@
|
|
1
|
+
class JdlTictactoe::Board
|
2
|
+
|
3
|
+
attr_reader :size, :squares
|
4
|
+
|
5
|
+
def initialize(size=3)
|
6
|
+
@size = size
|
7
|
+
@squares = initialize_squares(size)
|
8
|
+
end
|
9
|
+
|
10
|
+
def mark_square(square_index, marker)
|
11
|
+
row, column = translate_index(square_index)
|
12
|
+
squares[row][column] = marker
|
13
|
+
end
|
14
|
+
|
15
|
+
def undo_move(square_index)
|
16
|
+
row, column = translate_index(square_index)
|
17
|
+
squares[row][column] = nil
|
18
|
+
end
|
19
|
+
|
20
|
+
def value_at(square_index)
|
21
|
+
row, column = translate_index(square_index)
|
22
|
+
squares[row][column]
|
23
|
+
end
|
24
|
+
|
25
|
+
def winner?(mark)
|
26
|
+
horizontal_winner?(mark) || vertical_winner?(mark) || first_diagonal_winner?(mark) || second_diagonal_winner?(mark)
|
27
|
+
end
|
28
|
+
|
29
|
+
def draw?(player)
|
30
|
+
return true if empty_squares.length == 0 && ( !winner?(player.mark) || !winner?(player.opponent.mark) )
|
31
|
+
end
|
32
|
+
|
33
|
+
def empty_squares
|
34
|
+
@squares.flatten.each_with_index.collect { |sq, i| i+1 if sq == nil}.compact
|
35
|
+
end
|
36
|
+
|
37
|
+
def score(player)
|
38
|
+
return 1 if winner?(player.mark)
|
39
|
+
return -1 if winner?(player.opponent.mark)
|
40
|
+
return 0 if draw?(player)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def first_diagonal_winner?(mark)
|
46
|
+
diagonal = []
|
47
|
+
size.times do |row|
|
48
|
+
diagonal << squares[row][row]
|
49
|
+
end
|
50
|
+
winner_in_collection?(diagonal, mark)
|
51
|
+
end
|
52
|
+
|
53
|
+
def second_diagonal_winner?(mark)
|
54
|
+
diagonal = []
|
55
|
+
size.times do |row|
|
56
|
+
diagonal << squares[row][size - row - 1]
|
57
|
+
end
|
58
|
+
winner_in_collection?(diagonal, mark)
|
59
|
+
end
|
60
|
+
|
61
|
+
def vertical_winner?(mark)
|
62
|
+
size.times do |index|
|
63
|
+
column = []
|
64
|
+
size.times do |row|
|
65
|
+
column << squares[row][index]
|
66
|
+
end
|
67
|
+
return true if winner_in_collection?(column, mark)
|
68
|
+
end
|
69
|
+
false
|
70
|
+
end
|
71
|
+
|
72
|
+
def horizontal_winner?(mark)
|
73
|
+
squares.reduce(false) do |winner, row|
|
74
|
+
winner || winner_in_collection?(row, mark)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def winner_in_collection?(collection, mark)
|
79
|
+
!collection.include?(nil) && collection.uniq.size == 1 && collection.include?(mark)
|
80
|
+
end
|
81
|
+
|
82
|
+
def initialize_squares(size)
|
83
|
+
squares = []
|
84
|
+
size.times { squares << ([nil]*size) }
|
85
|
+
return squares
|
86
|
+
end
|
87
|
+
|
88
|
+
def translate_index(square_index)
|
89
|
+
square_index -= 1
|
90
|
+
row = square_index / size
|
91
|
+
column = square_index % size
|
92
|
+
return row, column
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class JdlTictactoe::Game
|
2
|
+
|
3
|
+
attr_accessor :player1, :player2, :turn, :board
|
4
|
+
|
5
|
+
def initialize(player1, player2, size=3)
|
6
|
+
@player1 = player1
|
7
|
+
@player2 = player2
|
8
|
+
@turn = :player1
|
9
|
+
@board = Board.new(size)
|
10
|
+
set_opponents(@player1, @player2)
|
11
|
+
end
|
12
|
+
|
13
|
+
def switch_turn(turn)
|
14
|
+
@turn = :player2 if turn == :player1
|
15
|
+
@turn = :player1 if turn == :player2
|
16
|
+
return @turn
|
17
|
+
end
|
18
|
+
|
19
|
+
def over?
|
20
|
+
return true if winner?(@player1.mark) || winner?(@player2.mark) || empty_squares.count == 0
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def set_opponents(player1, player2)
|
26
|
+
player1.opponent = player2
|
27
|
+
player2.opponent = player1
|
28
|
+
end
|
29
|
+
|
30
|
+
def method_missing(method_name, *args)
|
31
|
+
delegated_methods = [:empty_squares, :winner?, :mark_square, :undo_move]
|
32
|
+
if delegated_methods.include?(method_name)
|
33
|
+
self.board.send(method_name, *args)
|
34
|
+
else
|
35
|
+
raise NoMethodError.new("Undefined method #{method_name} called on Game")
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class JdlTictactoe::Player
|
2
|
+
|
3
|
+
attr_accessor :species, :mark, :opponent
|
4
|
+
|
5
|
+
def initialize(species, mark="X")
|
6
|
+
@species = "human" if species == "H"
|
7
|
+
@species = "computer" if species == "C"
|
8
|
+
@mark = mark
|
9
|
+
end
|
10
|
+
|
11
|
+
def mark_square(board, square)
|
12
|
+
board.mark_square(square, @mark)
|
13
|
+
end
|
14
|
+
|
15
|
+
def minimax(game, turn)
|
16
|
+
if game.empty_squares.count == 9
|
17
|
+
return 1
|
18
|
+
elsif game.empty_squares.count == 8
|
19
|
+
return 5 if game.empty_squares.include?(5)
|
20
|
+
return 1 if !game.empty_squares.include?(5)
|
21
|
+
else
|
22
|
+
return get_minimax_square(game, turn)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def get_minimax_scores(game, turn)
|
27
|
+
scores = []
|
28
|
+
return [game.board.score(self)] if game.over?
|
29
|
+
|
30
|
+
game.empty_squares.each do |move|
|
31
|
+
game.mark_square(move, game.send(turn).mark)
|
32
|
+
minimax_scores = get_minimax_scores(game, game.switch_turn(turn))
|
33
|
+
|
34
|
+
if game.send(turn) == self
|
35
|
+
scores << minimax_scores.min
|
36
|
+
elsif game.send(turn) != self
|
37
|
+
scores << minimax_scores.max
|
38
|
+
end
|
39
|
+
|
40
|
+
game.undo_move(move)
|
41
|
+
end
|
42
|
+
return scores
|
43
|
+
end
|
44
|
+
|
45
|
+
def get_minimax_square(game, turn)
|
46
|
+
moves_with_score = {}
|
47
|
+
scores = get_minimax_scores(game, turn)
|
48
|
+
|
49
|
+
game.empty_squares.each_with_index do |move, index|
|
50
|
+
moves_with_score[move] = scores[index]
|
51
|
+
end
|
52
|
+
|
53
|
+
max = moves_with_score.values.max
|
54
|
+
return moves_with_score.select { |k,v| v == max }.keys.first
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
data/lib/jdl_tictactoe.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jdl_tictactoe
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -18,6 +18,9 @@ extensions: []
|
|
18
18
|
extra_rdoc_files: []
|
19
19
|
files:
|
20
20
|
- lib/jdl_tictactoe.rb
|
21
|
+
- lib/jdl_tictactoe/board.rb
|
22
|
+
- lib/jdl_tictactoe/player.rb
|
23
|
+
- lib/jdl_tictactoe/game.rb
|
21
24
|
homepage: http://alwaysbcoding.com
|
22
25
|
licenses: []
|
23
26
|
post_install_message:
|