elus 0.1.0
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/.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
|