ta-te-ti 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/.gitignore +4 -0
- data/.travis.yml +8 -0
- data/Gemfile +4 -0
- data/Guardfile +5 -0
- data/README +17 -0
- data/Rakefile +38 -0
- data/bin/ta_te_ti.rb +8 -0
- data/lib/ta_te_ti.rb +20 -0
- data/lib/ta_te_ti/board.rb +108 -0
- data/lib/ta_te_ti/dumb_player.rb +25 -0
- data/lib/ta_te_ti/game.rb +33 -0
- data/lib/ta_te_ti/human_player.rb +29 -0
- data/lib/ta_te_ti/player.rb +16 -0
- data/lib/ta_te_ti/smart_player.rb +57 -0
- data/lib/ta_te_ti/squares_container.rb +7 -0
- data/lib/ta_te_ti/version.rb +3 -0
- data/spec/ta_te_ti_spec.rb +36 -0
- data/ta_te_ti.gemspec +24 -0
- data/test/tc_ta_te_ti.rb +23 -0
- metadata +76 -0
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
data/README
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
Practica de Laboratorio #9: Creacion de una Gema
|
2
|
+
|
3
|
+
Tic-Tac-Toe (Tres en raya)
|
4
|
+
|
5
|
+
Clases para implementar el juego de tres en raya
|
6
|
+
Utiliza el concepto de herencia.
|
7
|
+
Hay modulos y mixins.
|
8
|
+
|
9
|
+
Clase desarrollada con rspec siguiendo la filosofia TDD
|
10
|
+
Contiene tambien pruebas unitarias Unit Testing
|
11
|
+
La clase se compila con la herramienta Travis de integracion continua.
|
12
|
+
Se comprueba la portabilidad entre distintas plataformas y versiones de Ruby.
|
13
|
+
|
14
|
+
Se deben poner los ficheros en spec para que se ejecuten ante
|
15
|
+
cualquier modificacion
|
16
|
+
|
17
|
+
Se ha creado una Gema con bundle
|
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'rake/clean'
|
3
|
+
|
4
|
+
CLEAN.include('*.swp')
|
5
|
+
CLOBBER.include('pkg/*.gem')
|
6
|
+
|
7
|
+
task :default => [:spec,:test]
|
8
|
+
|
9
|
+
desc "Run ta_te_ti.rb"
|
10
|
+
task :bin do
|
11
|
+
sh "ruby -Ilib bin/ta_te_ti.rb"
|
12
|
+
end
|
13
|
+
|
14
|
+
desc "Run rspec for ta_te_ti.rb development"
|
15
|
+
task :spec do
|
16
|
+
sh "rspec -Ilib spec/ta_te_ti_spec.rb"
|
17
|
+
end
|
18
|
+
|
19
|
+
desc "Run test/tc_ta_te_ti.rb"
|
20
|
+
task :test do
|
21
|
+
sh "ruby -Ilib test/tc_ta_te_ti.rb"
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "Uninstall gem ta-te-ti"
|
25
|
+
task :uninstall do
|
26
|
+
sh "gem uninstall ta-te-ti"
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "Hide gem ta-te-ti in rubygems.org"
|
30
|
+
task :unpublish, :version do |args|
|
31
|
+
sh "gem yank ta-te-ti -v #{args[:version]}"
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "Show all published versions of gem ta-te-ti"
|
35
|
+
task :published do
|
36
|
+
sh "gem list ta-te-ti --remote --all"
|
37
|
+
end
|
38
|
+
|
data/bin/ta_te_ti.rb
ADDED
data/lib/ta_te_ti.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "ta_te_ti/board"
|
2
|
+
require "ta_te_ti/player"
|
3
|
+
require "ta_te_ti/human_player"
|
4
|
+
require "ta_te_ti/dumb_player"
|
5
|
+
require "ta_te_ti/smart_player"
|
6
|
+
require "ta_te_ti/game"
|
7
|
+
|
8
|
+
require "ta_te_ti/version"
|
9
|
+
|
10
|
+
module TaTeTi
|
11
|
+
end
|
12
|
+
|
13
|
+
if __FILE__ == $0
|
14
|
+
if ARGV.size > 0 and ARGV[0] == "-d"
|
15
|
+
game = TaTeTi::Game.new TaTeTi::HumanPlayer, TaTeTi::DumbPlayer
|
16
|
+
else
|
17
|
+
game = TaTeTi::Game.new TaTeTi::HumanPlayer, TaTeTi::SmartPlayer
|
18
|
+
end
|
19
|
+
game.play
|
20
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'ta_te_ti/squares_container'
|
2
|
+
|
3
|
+
module TaTeTi
|
4
|
+
class Board
|
5
|
+
attr_reader :squares
|
6
|
+
|
7
|
+
class Row
|
8
|
+
attr_reader :squares, :names
|
9
|
+
|
10
|
+
def initialize( squares, names )
|
11
|
+
@squares = squares
|
12
|
+
@names = names
|
13
|
+
end
|
14
|
+
|
15
|
+
include SquaresContainer
|
16
|
+
|
17
|
+
def to_board_name( index )
|
18
|
+
Board.index_to_name(@names[index])
|
19
|
+
end
|
20
|
+
|
21
|
+
def ==(other)
|
22
|
+
(@squares == other.squares) && (@names == other.names)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
MOVES = %w{a1 a2 a3 b1 b2 b3 c1 c2 c3}
|
27
|
+
# Define constant INDICES
|
28
|
+
INDICES = Hash.new { |h, k| h[k] = MOVES.find_index(k) }
|
29
|
+
|
30
|
+
def self.name_to_index( name )# Receives "b2" and returns 4
|
31
|
+
INDICES[name]
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.index_to_name( index ) # Receives the index, like 4 and returns "b2"
|
35
|
+
MOVES[index]
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize( squares )
|
39
|
+
@squares = squares # An array of Strings: [ " ", " ", " ", " ", "X", " ", " ", " ", "O"]
|
40
|
+
end
|
41
|
+
|
42
|
+
include SquaresContainer
|
43
|
+
|
44
|
+
def []( *indices )
|
45
|
+
if indices.size == 2 # board[1,2] is @squares[7]
|
46
|
+
super indices[0] + indices[1] * 3 # calls SquaresContainer [] method
|
47
|
+
elsif indices[0].is_a? Fixnum # board[7]
|
48
|
+
super indices[0]
|
49
|
+
else # board["b2"]
|
50
|
+
super Board.name_to_index(indices[0].to_s)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def []=(indice, value) # board["b2"] = "X"
|
55
|
+
m = Board.name_to_index(indice)
|
56
|
+
@squares[m] = value
|
57
|
+
end
|
58
|
+
|
59
|
+
HORIZONTALS = [ [0, 1, 2], [3, 4, 5], [6, 7, 8] ]
|
60
|
+
COLUMNS = [ [0, 3, 6], [1, 4, 7], [2, 5, 8] ]
|
61
|
+
DIAGONALS = [ [0, 4, 8], [2, 4, 6] ]
|
62
|
+
ROWS = HORIZONTALS + COLUMNS + DIAGONALS
|
63
|
+
|
64
|
+
def each_row
|
65
|
+
ROWS.each do |e|
|
66
|
+
yield Row.new(@squares.values_at(*e), e)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def moves
|
71
|
+
moves = [ ]
|
72
|
+
@squares.each_with_index do |s, i|
|
73
|
+
moves << Board.index_to_name(i) if s == " "
|
74
|
+
end
|
75
|
+
moves # returns the set of feasible moves [ "b3", "c2", ... ]
|
76
|
+
end
|
77
|
+
|
78
|
+
def won?
|
79
|
+
each_row do |row|
|
80
|
+
return "X" if row.xs == 3 # "X" wins
|
81
|
+
return "O" if row.os == 3 # "O" wins
|
82
|
+
end
|
83
|
+
return " " if blanks == 0 # tie
|
84
|
+
false
|
85
|
+
end
|
86
|
+
|
87
|
+
BOARD =<<EOS
|
88
|
+
|
89
|
+
+---+---+---+
|
90
|
+
a | 0 | 1 | 2 |
|
91
|
+
+---+---+---+
|
92
|
+
b | 3 | 4 | 5 |
|
93
|
+
+---+---+---+
|
94
|
+
c | 6 | 7 | 8 |
|
95
|
+
+---+---+---+
|
96
|
+
1 2 3
|
97
|
+
|
98
|
+
EOS
|
99
|
+
def to_s
|
100
|
+
BOARD.gsub(/(\d)(?= \|)/) { |i| @squares[i.to_i] }
|
101
|
+
end
|
102
|
+
|
103
|
+
def ==(other)
|
104
|
+
(@squares == other.squares)
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module TaTeTi
|
2
|
+
class DumbPlayer < Player
|
3
|
+
def move( board )
|
4
|
+
moves = board.moves
|
5
|
+
moves[rand(moves.size)]
|
6
|
+
end
|
7
|
+
|
8
|
+
def finish( final_board )
|
9
|
+
# print final_board
|
10
|
+
|
11
|
+
if final_board.won? == @mark
|
12
|
+
# print "Congratulations, you win.\n\n"
|
13
|
+
win = 1
|
14
|
+
elsif final_board.won? == " "
|
15
|
+
# print "Tie game.\n\n"
|
16
|
+
win = 0
|
17
|
+
else
|
18
|
+
# print "You lost tic-tac-toe?!\n\n"
|
19
|
+
win = -1
|
20
|
+
end
|
21
|
+
win
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'ta_te_ti/board'
|
2
|
+
|
3
|
+
module TaTeTi
|
4
|
+
class Game
|
5
|
+
def initialize( player1, player2, random = true )
|
6
|
+
if random and rand(2) == 1
|
7
|
+
@x_player = player2.new("X")
|
8
|
+
@o_player = player1.new("O")
|
9
|
+
else
|
10
|
+
@x_player = player1.new("X")
|
11
|
+
@o_player = player2.new("O")
|
12
|
+
end
|
13
|
+
|
14
|
+
@board = Board.new([" "] * 9)
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :x_player, :o_player
|
18
|
+
|
19
|
+
def play
|
20
|
+
until @board.won?
|
21
|
+
@board[@x_player.move(@board)] = @x_player.mark
|
22
|
+
break if @board.won?
|
23
|
+
|
24
|
+
@board[@o_player.move(@board)] = @o_player.mark
|
25
|
+
end
|
26
|
+
|
27
|
+
@o_player.finish @board
|
28
|
+
@x_player.finish @board
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module TaTeTi
|
2
|
+
class HumanPlayer < Player
|
3
|
+
def move( board )
|
4
|
+
print board
|
5
|
+
|
6
|
+
moves = board.moves
|
7
|
+
print "Your move? (format: b3) "
|
8
|
+
move = $stdin.gets
|
9
|
+
until moves.include?(move.chomp.downcase)
|
10
|
+
print "Invalid move. Try again. "
|
11
|
+
move = $stdin.gets
|
12
|
+
end
|
13
|
+
move.chomp
|
14
|
+
end
|
15
|
+
|
16
|
+
def finish( final_board )
|
17
|
+
print final_board
|
18
|
+
|
19
|
+
if final_board.won? == @mark
|
20
|
+
print "Congratulations, you win.\n\n"
|
21
|
+
elsif final_board.won? == " "
|
22
|
+
print "Tie game.\n\n"
|
23
|
+
else
|
24
|
+
print "You lost tic-tac-toe?!\n\n"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module TaTeTi
|
2
|
+
class Player
|
3
|
+
def initialize( mark )
|
4
|
+
@mark = mark # "X" or "O" or " "
|
5
|
+
end
|
6
|
+
|
7
|
+
attr_reader :mark
|
8
|
+
|
9
|
+
def move( board )
|
10
|
+
raise NotImplementedError, "Player subclasses must define move()."
|
11
|
+
end
|
12
|
+
|
13
|
+
def finish( final_board )
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module TaTeTi
|
2
|
+
class SmartPlayer < Player
|
3
|
+
def move( board )
|
4
|
+
moves = board.moves
|
5
|
+
|
6
|
+
# If I have a win, take it. If he is threatening to win, stop it.
|
7
|
+
board.each_row do |row|
|
8
|
+
if row.blanks == 1 and (row.xs == 2 or row.os == 2)
|
9
|
+
(0..2).each do |e|
|
10
|
+
return row.to_board_name(e) if row[e] == " "
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Take the center if open.
|
16
|
+
return "b2" if moves.include? "b2"
|
17
|
+
|
18
|
+
# Defend opposite corners.
|
19
|
+
if board[0] != @mark and board[0] != " " and board[8] == " "
|
20
|
+
return "c3"
|
21
|
+
elsif board[8] != @mark and board[8] != " " and board[0] == " "
|
22
|
+
return "a1"
|
23
|
+
elsif board[2] != @mark and board[2] != " " and board[6] == " "
|
24
|
+
return "c1"
|
25
|
+
elsif board[6] != @mark and board[6] != " " and board[2] == " "
|
26
|
+
return "a3"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Defend against the special case XOX on a diagonal.
|
30
|
+
if board.xs == 2 and board.os == 1 and board[4] == "O" and
|
31
|
+
(board[0] == "X" and board[8] == "X") or
|
32
|
+
(board[2] == "X" and board[6] == "X")
|
33
|
+
return %w{a2 b1 b3 c2}[rand(4)]
|
34
|
+
end
|
35
|
+
|
36
|
+
# Or make a random move.
|
37
|
+
moves[rand(moves.size)]
|
38
|
+
end
|
39
|
+
|
40
|
+
def finish( final_board )
|
41
|
+
print final_board
|
42
|
+
|
43
|
+
if final_board.won? == @mark
|
44
|
+
print "Congratulations, you win.\n\n"
|
45
|
+
win = 1
|
46
|
+
elsif final_board.won? == " "
|
47
|
+
print "Tie game.\n\n"
|
48
|
+
win = 0
|
49
|
+
else
|
50
|
+
print "You lost tic-tac-toe?!\n\n"
|
51
|
+
win = -1
|
52
|
+
end
|
53
|
+
win
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'ta_te_ti'
|
2
|
+
|
3
|
+
describe do
|
4
|
+
before :all do
|
5
|
+
@fila = TaTeTi::Board::Row.new(["0","X",""],[0,1,2])
|
6
|
+
@board = TaTeTi::Board.new([ " ", " ", " ", " ", "X", " ", " ", " ","O"])
|
7
|
+
@human_player = TaTeTi::HumanPlayer.new("X")
|
8
|
+
@dumb_player = TaTeTi::DumbPlayer.new("0")
|
9
|
+
@smart_player = TaTeTi::SmartPlayer.new("0")
|
10
|
+
@gameD = TaTeTi::Game.new TaTeTi::SmartPlayer, TaTeTi::DumbPlayer
|
11
|
+
end
|
12
|
+
|
13
|
+
it "Debe existir una clase para representar una fila del tablero" do
|
14
|
+
@fila.should == TaTeTi::Board::Row.new(["0","X",""],[0,1,2])
|
15
|
+
end
|
16
|
+
|
17
|
+
it "Debe existir una clase para representar al tablero" do
|
18
|
+
@board.should == TaTeTi::Board.new([ " ", " ", " ", " ", "X", " ", " ", " ","O"])
|
19
|
+
end
|
20
|
+
|
21
|
+
it "Debe existir una clase para representar al jugador humano" do
|
22
|
+
@human_player.mark.should == "X"
|
23
|
+
end
|
24
|
+
|
25
|
+
it "Debe existir una clase para representar un juego simple de la maquina" do
|
26
|
+
@dumb_player.mark.should == "0"
|
27
|
+
end
|
28
|
+
|
29
|
+
it "Debe existir una clase para representar un juego inteligente de la maquina" do
|
30
|
+
@smart_player.mark.should == "0"
|
31
|
+
end
|
32
|
+
|
33
|
+
it "Debe existir una clase para representar un juego" do
|
34
|
+
[-1,0,1].should include(@gameD.play)
|
35
|
+
end
|
36
|
+
end
|
data/ta_te_ti.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "ta_te_ti/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "ta-te-ti"
|
7
|
+
s.version = TaTeTi::VERSION
|
8
|
+
s.authors = ["Coromoto Leon"]
|
9
|
+
s.email = ["cleon@ull.es"]
|
10
|
+
s.homepage = ""
|
11
|
+
s.summary = %q{Gema para jugar al tres en raya}
|
12
|
+
s.description = %q{Juego del tres en raya}
|
13
|
+
|
14
|
+
s.rubyforge_project = "ta_te_ti"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
s.add_development_dependency "rspec"
|
23
|
+
# s.add_runtime_dependency "rest-client"
|
24
|
+
end
|
data/test/tc_ta_te_ti.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'ta_te_ti'
|
2
|
+
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
class JuegoTaTeTi < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def setup
|
8
|
+
# si se define una variable de instancia, el juego no se actualiza aleatoriamente porqu solo hay una
|
9
|
+
end
|
10
|
+
|
11
|
+
def teardown
|
12
|
+
#nada, la memoria se libera automaticamente
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_play
|
16
|
+
score = []
|
17
|
+
30.times do
|
18
|
+
ter_obj = TaTeTi::Game.new TaTeTi::DumbPlayer, TaTeTi::DumbPlayer
|
19
|
+
score.push ter_obj.play
|
20
|
+
end
|
21
|
+
assert(score.uniq.length == 3, "score.uniq.length = #{score.uniq.length} score.length = #{score.length}")
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ta-te-ti
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Coromoto Leon
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-21 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &77550420 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *77550420
|
25
|
+
description: Juego del tres en raya
|
26
|
+
email:
|
27
|
+
- cleon@ull.es
|
28
|
+
executables:
|
29
|
+
- ta_te_ti.rb
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- .gitignore
|
34
|
+
- .travis.yml
|
35
|
+
- Gemfile
|
36
|
+
- Guardfile
|
37
|
+
- README
|
38
|
+
- Rakefile
|
39
|
+
- bin/ta_te_ti.rb
|
40
|
+
- lib/ta_te_ti.rb
|
41
|
+
- lib/ta_te_ti/board.rb
|
42
|
+
- lib/ta_te_ti/dumb_player.rb
|
43
|
+
- lib/ta_te_ti/game.rb
|
44
|
+
- lib/ta_te_ti/human_player.rb
|
45
|
+
- lib/ta_te_ti/player.rb
|
46
|
+
- lib/ta_te_ti/smart_player.rb
|
47
|
+
- lib/ta_te_ti/squares_container.rb
|
48
|
+
- lib/ta_te_ti/version.rb
|
49
|
+
- spec/ta_te_ti_spec.rb
|
50
|
+
- ta_te_ti.gemspec
|
51
|
+
- test/tc_ta_te_ti.rb
|
52
|
+
homepage: ''
|
53
|
+
licenses: []
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options: []
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ! '>='
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
requirements: []
|
71
|
+
rubyforge_project: ta_te_ti
|
72
|
+
rubygems_version: 1.8.11
|
73
|
+
signing_key:
|
74
|
+
specification_version: 3
|
75
|
+
summary: Gema para jugar al tres en raya
|
76
|
+
test_files: []
|