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.
@@ -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
@@ -1,5 +1,7 @@
1
1
  class JdlTictactoe
2
- def self.hi
3
- puts "Hello World!"
4
- end
2
+
5
3
  end
4
+
5
+ require 'jdl_tictactoe/board'
6
+ require 'jdl_tictactoe/player'
7
+ require 'jdl_tictactoe/game'
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.1
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: