elus 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +30 -0
- data/Rakefile +57 -0
- data/VERSION +1 -0
- data/bin/elus +8 -0
- data/doc/dev_plan.htm +923 -0
- data/doc/dev_plan_files/colorschememapping.xml +2 -0
- data/doc/dev_plan_files/filelist.xml +6 -0
- data/doc/dev_plan_files/themedata.thmx +0 -0
- data/doc/user_stories.htm +959 -0
- data/doc/user_stories_files/colorschememapping.xml +2 -0
- data/doc/user_stories_files/filelist.xml +6 -0
- data/doc/user_stories_files/themedata.thmx +0 -0
- data/elus.gemspec +91 -0
- data/features/gamer_inputs_state.feature +135 -0
- data/features/gamer_starts_solver.feature +13 -0
- data/features/gamer_updates_state.feature +27 -0
- data/features/solver_shows_hints.feature +36 -0
- data/features/step_definitions/elus_steps.rb +116 -0
- data/features/support/env.rb +5 -0
- data/features/support/stats.rb +32 -0
- data/lib/elus.rb +5 -0
- data/lib/elus/game.rb +74 -0
- data/lib/elus/generator.rb +48 -0
- data/lib/elus/piece.rb +93 -0
- data/lib/elus/rule.rb +36 -0
- data/lib/elus/solver.rb +84 -0
- data/spec/cucumber/stats_spec.rb +13 -0
- data/spec/elus/game_spec.rb +116 -0
- data/spec/elus/generator_spec.rb +124 -0
- data/spec/elus/piece_spec.rb +190 -0
- data/spec/elus/rule_spec.rb +39 -0
- data/spec/elus/solver_spec.rb +203 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +198 -0
- metadata +117 -0
data/lib/elus.rb
ADDED
data/lib/elus/game.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
module Elus
|
2
|
+
class Game
|
3
|
+
def initialize(options = {})
|
4
|
+
rules_generator = options[:generator]
|
5
|
+
@board = options[:board]
|
6
|
+
@free = options[:free]
|
7
|
+
raise Elus::Invalid, "Wrong Board or Free set: #{@board}, #{@free}" unless [@board, @free].all? {|set| Array === set} # Check if all pieces are correct
|
8
|
+
raise Elus::Invalid, "Wrong Game Pieces: #{@board}, #{@free}" unless (@board + @free).all? {|piece| Piece === piece} # Check if all pieces are correct
|
9
|
+
raise Elus::Invalid, "Wrong number of Game Pieces: #{@board}, #{@free}" unless @board.size >= 3 and @free.size == 3 # Check for correct Board/Free size
|
10
|
+
raise Elus::Invalid, "Wrong Rules generator" unless rules_generator.respond_to? :generate_rules
|
11
|
+
|
12
|
+
@rules = rules_generator.generate_rules
|
13
|
+
test_rules
|
14
|
+
count_moves
|
15
|
+
end
|
16
|
+
|
17
|
+
# Count Moves
|
18
|
+
def count_moves
|
19
|
+
@moves = @free.map do |piece|
|
20
|
+
count = @rules.count {|rule| piece == rule.apply(@board.last)}
|
21
|
+
count > 0 ? "#{piece.name}(#{count})" : nil
|
22
|
+
end.compact
|
23
|
+
end
|
24
|
+
|
25
|
+
def move(piece, new_free=nil)
|
26
|
+
if new_free # The move was right!
|
27
|
+
@board << piece
|
28
|
+
@free = new_free
|
29
|
+
else
|
30
|
+
@free.delete(piece)
|
31
|
+
end
|
32
|
+
test_rules
|
33
|
+
count_moves
|
34
|
+
end
|
35
|
+
|
36
|
+
def state
|
37
|
+
"Free:\n" + @free.join("\n") + "\nBoard:\n" + @board.join("\n") + "\n"
|
38
|
+
end
|
39
|
+
|
40
|
+
def hint
|
41
|
+
"Rules(#{@rules.size}):\n" + @rules.map(&:name).join("\n") +"\n" +
|
42
|
+
"Moves(#{@moves.size}):\n" + @moves.join("\n") + "\n"
|
43
|
+
end
|
44
|
+
|
45
|
+
def finished?
|
46
|
+
@board.size>=8
|
47
|
+
end
|
48
|
+
|
49
|
+
def valid_move? piece
|
50
|
+
@free.include? piece
|
51
|
+
end
|
52
|
+
private
|
53
|
+
|
54
|
+
# Tests rules against current Game state, drops inconsistent rules
|
55
|
+
def test_rules
|
56
|
+
# Drop rule if not consistent with the Board sequence
|
57
|
+
@rules.delete_if do |rule|
|
58
|
+
@board.each_cons(2).to_a.inject(false) do |delete, pieces|
|
59
|
+
delete or rule.apply(pieces.first)!=pieces.last
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Drop rule if applied to last Board Piece does not have single match among Free Pieces
|
64
|
+
@rules.delete_if do |rule|
|
65
|
+
@free.count {|piece| piece == rule.apply(@board.last)} != 1
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
# An exception of this class indicates invalid input
|
72
|
+
class Invalid < StandardError
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Elus
|
2
|
+
# Rules Generators
|
3
|
+
class Generator
|
4
|
+
def generate_rules
|
5
|
+
[]
|
6
|
+
end
|
7
|
+
|
8
|
+
# iterates through all permutations of string chars with 2 dots
|
9
|
+
def permutate(string='01!=')
|
10
|
+
string.split(//).product(['.'],['.']).map {|set| set.permutation.to_a.uniq}.flatten(1).map {|chars| chars.join}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class EmptyGenerator < Generator
|
15
|
+
end
|
16
|
+
|
17
|
+
class Turn1Generator < Generator
|
18
|
+
def generate_rules
|
19
|
+
yes_branches = permutate
|
20
|
+
['...'].product(yes_branches).map do |condition, yes|
|
21
|
+
Rule.new(Piece.create(condition), Piece.create(yes))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Turn2Generator < Generator
|
27
|
+
def generate_rules
|
28
|
+
conditions = permutate('1')
|
29
|
+
branches = permutate.map {|code| [code, Piece.different(code)]}
|
30
|
+
conditions.product(branches.uniq).map do |condition, yes_no|
|
31
|
+
yes,no = yes_no
|
32
|
+
Rule.new(Piece.create(condition), Piece.create(yes), Piece.create(no))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Turn3Generator < Generator
|
38
|
+
def generate_rules
|
39
|
+
conditions = permutate('1')
|
40
|
+
yes_branches = permutate
|
41
|
+
no_branches = permutate
|
42
|
+
conditions.product(yes_branches, no_branches).map do|condition, yes, no|
|
43
|
+
Rule.new(Piece.create(condition), Piece.create(yes), Piece.create(no))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
data/lib/elus/piece.rb
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
module Elus
|
2
|
+
class Piece
|
3
|
+
# These constants are used for translating between the external string representation of a Game and the internal representation.
|
4
|
+
VALID = "01sbgycdr=!." # Valid (meaningful) chars in code string (downcase)
|
5
|
+
SORT = "01stghcdd=!." # Special unique chars good for sorting (B)ig->(T)itanic, (Y)ellow->(H)ellow, (R)hombus->(D)iamond
|
6
|
+
FINAL = "010101011=!." # Final (place-dependent) representation as 0/1 digits
|
7
|
+
INVALID = Regexp.new "[^#{VALID}]" # Regexp matching all non-valid chars
|
8
|
+
PATTERN = /^[01st=!.][01gh=!.][01cd=!.]$/ # Correct code pattern for Piece creation
|
9
|
+
# Names for Piece characteristics (based on code)
|
10
|
+
NAMES = [ {'0'=>'Small', '1'=>'Big', '='=>'Same size', '!'=>'Different size'},
|
11
|
+
{'0'=>'Green', '1'=>'Yellow', '='=>'Same color', '!'=>'Different color'},
|
12
|
+
{'0'=>'Circle', '1'=>'Diamond', '='=>'Same shape', '!'=>'Different shape'} ]
|
13
|
+
|
14
|
+
attr_reader :code
|
15
|
+
private_class_method :new
|
16
|
+
|
17
|
+
# Factory method: takes an input string and tries to convert it into valid Piece code. Returns new Piece if successful, otherwise nil
|
18
|
+
def Piece.create(input)
|
19
|
+
return nil unless String === input
|
20
|
+
if input_code = convert_code(input) then new(input_code) else nil end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Pre-processes string into valid code for Piece creation
|
24
|
+
def Piece.convert_code(input)
|
25
|
+
# Remove all invalid chars from input and transcode it into sort chars
|
26
|
+
input_code = input.downcase.gsub(INVALID, '').tr(VALID, SORT)
|
27
|
+
# Remove dupes and sort unless code contains digits or special chars (place-dependent)
|
28
|
+
input_code = input_code.scan(/\w/).uniq.sort.reverse.join unless input_code =~ /[01!=.]/
|
29
|
+
# Translate sort chars into final chars
|
30
|
+
input_code.tr!(SORT, FINAL) if input_code =~ PATTERN
|
31
|
+
end
|
32
|
+
|
33
|
+
# Finds different (opposite, complimentary) code character
|
34
|
+
def Piece.different(string)
|
35
|
+
string.tr('01!=.', '10=!.')
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns Any Piece (should be a singleton object)
|
39
|
+
def Piece.any
|
40
|
+
@@any ||= Piece.create('...')
|
41
|
+
end
|
42
|
+
|
43
|
+
# Assumes valid code (should be pre-filtered by Piece.create)
|
44
|
+
def initialize(input_code)
|
45
|
+
@code = input_code
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns full text name of this Piece
|
49
|
+
def name
|
50
|
+
(0..2).map {|i| NAMES[i][@code[i]]}.compact.join(' ').
|
51
|
+
gsub(Regexp.new('^$'), 'Any').
|
52
|
+
gsub(Regexp.new('Same size Same color Same shape'), 'All Same').
|
53
|
+
gsub(Regexp.new('Different size Different color Different shape'), 'All Different')
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_s; name end
|
57
|
+
|
58
|
+
def * (other)
|
59
|
+
new_code = case other
|
60
|
+
when nil then nil
|
61
|
+
when String then self * Piece.create(other)
|
62
|
+
when Piece then
|
63
|
+
(0..2).map do |i|
|
64
|
+
case other.code[i]
|
65
|
+
when '=' then code[i]
|
66
|
+
when '!' then Piece.different code[i]
|
67
|
+
else other.code[i]
|
68
|
+
end
|
69
|
+
end.join
|
70
|
+
else raise(ArgumentError, 'Piece compared with a wrong type')
|
71
|
+
end
|
72
|
+
Piece.create(new_code)
|
73
|
+
end
|
74
|
+
|
75
|
+
include Comparable
|
76
|
+
|
77
|
+
def <=> (other)
|
78
|
+
case other
|
79
|
+
when nil then 1
|
80
|
+
when String then self <=> Piece.create(other)
|
81
|
+
when Piece then
|
82
|
+
return 0 if code == other.code
|
83
|
+
return 0 if code =~ Regexp.new(other.code)
|
84
|
+
return 0 if other.code =~ Regexp.new(code)
|
85
|
+
else raise(ArgumentError, 'Piece compared with a wrong type')
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def eql? (other)
|
90
|
+
(Piece === other) ? @code.eql?(other.code) : false
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/lib/elus/rule.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Elus
|
2
|
+
class Rule
|
3
|
+
def initialize(condition, yes, no=nil)
|
4
|
+
@condition = Piece === condition ? condition : Piece.create(condition)
|
5
|
+
raise Invalid, "Wrong condition" unless @condition
|
6
|
+
@yes = Piece === yes ? yes : Piece.create(yes)
|
7
|
+
raise Invalid, "Wrong yes branch" unless @yes
|
8
|
+
@no = if Piece === no
|
9
|
+
no
|
10
|
+
elsif Piece.create(no)
|
11
|
+
Piece.create(no)
|
12
|
+
elsif no
|
13
|
+
raise( Invalid, "Wrong no branch")
|
14
|
+
else
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns full text name of this Rule
|
20
|
+
def name
|
21
|
+
"If last Piece is #{@condition.name} Piece, #{@yes.name} Piece is next" + if @no then ", otherwise #{@no.name} Piece is next" else "" end
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s; name end
|
25
|
+
|
26
|
+
def apply(other)
|
27
|
+
piece = Piece === other ? other : Piece.create(other)
|
28
|
+
return nil unless piece
|
29
|
+
if piece == @condition
|
30
|
+
piece * @yes
|
31
|
+
else
|
32
|
+
piece * @no
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/elus/solver.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
module Elus
|
2
|
+
class Solver
|
3
|
+
attr_reader :game
|
4
|
+
|
5
|
+
def initialize(stdin, stdout)
|
6
|
+
@stdin = stdin
|
7
|
+
@stdout = stdout
|
8
|
+
end
|
9
|
+
|
10
|
+
def start (generator)
|
11
|
+
@generator = generator
|
12
|
+
@stdout.puts "Welcome to Elus Solver!"
|
13
|
+
@stdout.puts "Enter Game state:"
|
14
|
+
end
|
15
|
+
|
16
|
+
# Inputs Game state either from given code string or interactively (from @stdin)
|
17
|
+
def input_state(codes=nil)
|
18
|
+
if codes
|
19
|
+
pieces = codes.split("\n").map {|code| Piece.create(code)}.compact
|
20
|
+
free=pieces[0..2]
|
21
|
+
board=pieces[3..-1]
|
22
|
+
else
|
23
|
+
free = input_pieces "Free", 3
|
24
|
+
board = input_pieces "Board"
|
25
|
+
end
|
26
|
+
@game = Game.new(:free=>free, :board=>board, :generator=>@generator)
|
27
|
+
end
|
28
|
+
|
29
|
+
def input_pieces(label, num=10)
|
30
|
+
pieces = []
|
31
|
+
while pieces.size < num and piece = input_piece(:prompt => "Enter #{label} Piece code (#{pieces.size+1}):") do
|
32
|
+
@stdout.puts "You entered #{label} Piece (#{pieces.size+1}): #{piece.name}"
|
33
|
+
pieces << piece
|
34
|
+
end
|
35
|
+
pieces
|
36
|
+
end
|
37
|
+
|
38
|
+
# Inputs single correct code from stdin, returns correct piece or nil if break requested. Rejects wrong codes.
|
39
|
+
def input_piece(options = {})
|
40
|
+
loop do
|
41
|
+
@stdout.puts options[:prompt] || "Enter code:"
|
42
|
+
code = @stdin.gets
|
43
|
+
return nil if code == "\n"
|
44
|
+
if piece = Piece.create(code)
|
45
|
+
return piece
|
46
|
+
else
|
47
|
+
@stdout.puts options[:failure] || "Invalid code: #{code}"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def make_moves
|
53
|
+
while not @game.finished?
|
54
|
+
make_move
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def make_move
|
59
|
+
@stdout.puts @game.state
|
60
|
+
@stdout.puts @game.hint
|
61
|
+
piece = input_piece(:prompt => "Make your move:")
|
62
|
+
if piece and @game.valid_move? piece
|
63
|
+
@stdout.puts "You moved: #{piece.name}"
|
64
|
+
@stdout.puts "Was the move right(Y/N)?:"
|
65
|
+
if @stdin.gets =~ /[Yy]/
|
66
|
+
@stdout.puts "Great, now enter new Free set:"
|
67
|
+
free = input_pieces "Free", 3
|
68
|
+
@game.move piece, free
|
69
|
+
else
|
70
|
+
@stdout.puts "Too bad"
|
71
|
+
@game.move piece
|
72
|
+
end
|
73
|
+
elsif piece
|
74
|
+
@stdout.puts "Wrong move (not in free set): #{piece.name}"
|
75
|
+
else
|
76
|
+
@stdout.puts "Wrong move (no piece given)"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def state; @game.state end
|
81
|
+
def hint; @game.hint end
|
82
|
+
|
83
|
+
end #Solver
|
84
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), ".." ,"spec_helper" )
|
2
|
+
require File.join(File.dirname(__FILE__), ".." ,".." ,"features" ,"support" ,"stats" )
|
3
|
+
|
4
|
+
module ElusTest
|
5
|
+
# describe Stats do
|
6
|
+
# it 'ignores messages that do not contain relevant data' do
|
7
|
+
# stats = Stats.new ['BYD', 'BYD', 'BYD'], ElusTest::CODES
|
8
|
+
# stats.puts "Small Yellow Diamond"
|
9
|
+
# stats.count_for('BYD').should == 0
|
10
|
+
# stats.count_for('SYD').should == nil
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), ".." ,"spec_helper" )
|
2
|
+
|
3
|
+
module Elus
|
4
|
+
include ElusTest
|
5
|
+
|
6
|
+
def generator_stub
|
7
|
+
stub('generator', :generate_rules => [])
|
8
|
+
end
|
9
|
+
|
10
|
+
def should_be_in(string, *messages)
|
11
|
+
messages.each do |msg|
|
12
|
+
string.split("\n").should include(msg)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe Game do
|
17
|
+
before :each do
|
18
|
+
@free = [ Piece.create("BGC"),
|
19
|
+
Piece.create("sgd"),
|
20
|
+
Piece.create("syc") ]
|
21
|
+
@board = [Piece.create("BYD"),
|
22
|
+
Piece.create("SYD"),
|
23
|
+
Piece.create("BGD") ]
|
24
|
+
@pieces = @free + @board
|
25
|
+
@new = [ Piece.create("BYC"),
|
26
|
+
Piece.create("BYD"),
|
27
|
+
Piece.create("SGC") ]
|
28
|
+
end
|
29
|
+
|
30
|
+
context "creating" do
|
31
|
+
it "should raise exception if not enough Pieces" do
|
32
|
+
lambda{Game.new(:generator=>generator_stub)}.should raise_error(Invalid)
|
33
|
+
lambda{Game.new(:free=>@free, :generator=>generator_stub)}.should raise_error(Invalid)
|
34
|
+
lambda{Game.new(:board=>@board,:generator=>generator_stub)}.should raise_error(Invalid)
|
35
|
+
(0..2).each {|n| lambda{Game.new(:free=>@pieces[0,n], :board=>@board, :generator=>@wrong_generator)}.should raise_error(Invalid)}
|
36
|
+
(0..2).each {|n| lambda{Game.new(:free=>@free, :board=>@pieces[3,n], :generator=>@wrong_generator)}.should raise_error(Invalid)}
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should raise exception if it is given wrong generator_stub" do
|
40
|
+
lambda{Game.new(:free=>@free, :board=>@board, :generator=>@wrong_generator)}.should raise_error(Invalid)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should contain Free, Board and matching Piece name in Game state" do
|
44
|
+
CODES.each do |code, name|
|
45
|
+
code1, name1 = CODES.to_a[rand(CODES.size-1)]
|
46
|
+
free = @pieces[0..1] << Piece.create(code)
|
47
|
+
board = @pieces[2..3] << Piece.create(code1)
|
48
|
+
game = Game.new(:free=>free, :board=>board, :generator=>generator_stub)
|
49
|
+
should_be_in game.state, name, name1, "Free:", "Board:"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context 'generating hints' do
|
55
|
+
it "should generate appropriate hints about Rules" do
|
56
|
+
game = Game.new(:free=>@free, :board=>@board, :generator=>Turn1Generator.new)
|
57
|
+
should_be_in game.hint, 'Rules(2):',
|
58
|
+
'If last Piece is Any Piece, Diamond Piece is next',
|
59
|
+
'If last Piece is Any Piece, Same shape Piece is next'
|
60
|
+
end
|
61
|
+
it "should generate appropriate hints about Moves" do
|
62
|
+
game = Game.new(:free=>@free, :board=>@board, :generator=>Turn1Generator.new)
|
63
|
+
should_be_in game.hint, 'Moves(1):',
|
64
|
+
'Small Green Diamond(2)'
|
65
|
+
end
|
66
|
+
end
|
67
|
+
context 'making moves' do
|
68
|
+
it 'should add moved piece to board upon right move' do
|
69
|
+
game = Game.new(:free=>@free, :board=>@board, :generator=>Turn1Generator.new)
|
70
|
+
piece = Piece.create('SGD')
|
71
|
+
game.move(piece, @new)
|
72
|
+
game.state.should =~ Regexp.new("Board:[\\w\\s]*#{piece.name}")
|
73
|
+
end
|
74
|
+
it 'should reset free and moves sets upon right move' do
|
75
|
+
game = Game.new(:free=>@free, :board=>@board, :generator=>Turn1Generator.new)
|
76
|
+
piece = Piece.create('SGD')
|
77
|
+
game.move(piece, @new)
|
78
|
+
game.state.should =~ Regexp.new('Free:\s'+@new.map(&:name).join('\s'))
|
79
|
+
game.hint.should =~ /Moves\(1\):\sBig Yellow Diamond/
|
80
|
+
end
|
81
|
+
it 'should not add moved piece to board upon wrong move' do
|
82
|
+
game = Game.new(:free=>@free, :board=>@board, :generator=>Turn1Generator.new)
|
83
|
+
piece = Piece.create('BGC')
|
84
|
+
game.move(piece)
|
85
|
+
game.state.should_not =~ Regexp.new("Board:[\\w\\s]*#{piece.name}")
|
86
|
+
end
|
87
|
+
it 'should remove moved piece from free set upon wrong move' do
|
88
|
+
game = Game.new(:free=>@free, :board=>@board, :generator=>Turn1Generator.new)
|
89
|
+
piece = Piece.create('BGC')
|
90
|
+
game.move(piece)
|
91
|
+
game.state.should_not =~ Regexp.new('Free:[\s.]*'+piece.name+'[\s.]Board')
|
92
|
+
game.hint.should =~ /Moves\(1\):\sSmall Green Diamond/
|
93
|
+
end
|
94
|
+
end
|
95
|
+
context 'predicate testing' do
|
96
|
+
it 'should be finished if more than 8 pieces on the board' do
|
97
|
+
game = Game.new(:free=>@free, :board=>@board+@board+@board, :generator=>Turn1Generator.new)
|
98
|
+
game.should be_finished
|
99
|
+
end
|
100
|
+
it 'should consider any free piece a valid move' do
|
101
|
+
['BGC', 'SGD', 'SYC'].each do |code|
|
102
|
+
game = Game.new(:free=>@free, :board=>@board, :generator=>Turn1Generator.new)
|
103
|
+
piece = Piece.create(code)
|
104
|
+
game.should be_valid_move(piece)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
it 'should not consider any non-free piece a valid move' do
|
108
|
+
CODES.each do |code, name|
|
109
|
+
game = Game.new(:free=>@free, :board=>@board, :generator=>Turn1Generator.new)
|
110
|
+
piece = Piece.create(code)
|
111
|
+
game.should_not be_valid_move(piece) unless @free.include? piece
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|