jdl_tictactoe 0.0.1 → 0.0.2

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