require "test/unit"
require "jg.rb"

class TestJGP < Test::Unit::TestCase
    
    def setup
        @p = JapaneseGamePosition[ [:a, :b], [:c, :d], [:e, :f]]
        @b1 = JapaneseGamePosition::BANK1
        @b2 = JapaneseGamePosition::BANK2
    end
    
    
    def test_initializer
        assert_equal [:a, :b], @p.bank1, "bank1"
        assert_equal [:c, :d], @p.boat,  "boat"
        assert_equal [:e, :f], @p.bank2, "bank2"
    end

    def test_banks        
        assert_equal @b2, @p.opposite_bank( @b1 )
        assert_equal @b1, @p.opposite_bank( @b2 )
        assert_equal [:a, :b], @p[@b1]
        assert_equal [:e, :f], @p[@b2]
        
    end
    
    
    def test_clone
        q = @p.clone
        
        assert_equal [:a, :b], q.bank1, "bank1"
        assert_equal [:c, :d], q.boat,  "boat"
        assert_equal [:e, :f], q.bank2, "bank2"
        
        q.bank1 << :B
        q.boat  << :D
        q.bank2 << :F
        
        # q should change
        assert_equal [:a, :b, :B], q.bank1, "bank1"
        assert_equal [:c, :d, :D], q.boat,  "boat"
        assert_equal [:e, :f, :F], q.bank2, "bank2"
        
        # but original @p should remain as unchanged
        assert_equal [:a, :b], @p.bank1, "bank1"
        assert_equal [:c, :d], @p.boat,  "boat"
        assert_equal [:e, :f], @p.bank2, "bank2"

    end
    
    def test_embark_from_bank        
        test_cases = [
            [[ [:a, :b, :c, :d],[],[]],   [:a,:b],  [ [:c, :d],[:a, :b],[]]  ],
            [[ [:a, :b, :c, :d],[],[]],   [:a,:b],  [ [:c, :d],[:a, :b],[]]  ],
            [[ [:a, :b, :c, :d],[],[]],   [:a   ],  [ [:b, :c, :d],[:a],[]]  ],
            [[ [:a, :b, :c, :d],[],[]],   [:a,:c],  [ [:b, :d],[:a, :c],[]]  ],
        ]
        
        test_cases.each{|position, embark, end_position|
            q = JapaneseGamePosition[ *position ]
            r = JapaneseGamePosition[ *end_position ]
            
            q.embark_from_bank( @b1, embark )
            q.resort
            assert_equal r, q
        }
    end
 
    def test_embark_from_bank_2       
        test_cases = [
            [[ [:a, :b, :c, :d],[],[]],   [:a,:f], ],
            [[ [:a, :b, :c, :d],[],[]],   [:e,:f], ],
            [[ [:a, :b, :c, :d],[],[]],   [],      ],
            [[ [:a, :b, :c, :d],[],[]],   [:a,:b,:c]],
            [[ [:a, :b, :c, :d],[:e],[]], [:a,:b],  ],     #non-empty boat
        ]
        
        test_cases.each{|position, embark|
            q = JapaneseGamePosition[ *position ]
            assert_raises(ArgumentError){ q.embark_from_bank( @b1, embark ) }
        }
        
        assert_raises(ArgumentError){ @p.embark_from_bank( JapaneseGamePosition::BOAT, [:a] ) }
    end
 
    def test_disembark_to_bank
        test_cases = [
            [[ [],[:a, :b],[:c, :d]],      [ [],[],[:c, :d,:a, :b]], "two"  ],
            [[ [],[:a],[:b, :c, :d]],      [ [],[],[:c, :d,:a, :b]], "one"  ],
            [[ [],[:a],[:b, :c, :d]],      [ [],[],[:c, :d,:a, :b]], "one"  ],
        ]
    
        test_cases.each{|position, end_position, test_name|
            q = JapaneseGamePosition[ *position ]
            r = JapaneseGamePosition[ *end_position ]
            
            q.disembark_to_bank( @b2 )
            q.resort
            assert_equal r, q, test_name
        }
    end

    def test_disembark_to_bank_2
        test_cases = [
            [[ [],[  ],[:b, :c, :d]],      "empty boat"  ],
            [[ [],[:b],[:b, :c, :d]],      "duplicate actor"  ],
            [[ [],[:a, :b, :e],[:c, :d]],  "too many actors in boat"  ],
        ]
    
        test_cases.each{|position, test_name|
            q = JapaneseGamePosition[ *position ]
            assert_raises(ArgumentError, test_name){ 
                q.disembark_to_bank( @b2 )
            }
        }
        
        assert_raises(ArgumentError){ @p.disembark_to_bank( JapaneseGamePosition::BOAT ) }
    end
    
    def test_resort
        q = JapaneseGamePosition[ [:b, :a], [:c, :d], [:f, :e]]
        # The output will be sorted :a,b     :c, :d    :e  :f
        
        assert_equal @p, q
    end
    
    def test_to_s
        str = <<END
-> [:a, :b]
   [:c, :d]
   [:e, :f]
END
        assert_equal str, @p.to_s
        
        @p.boat.delete_if{true}
        
        str = <<END
-> [:a, :b]
   [:e, :f]
END
        assert_equal str, @p.to_s
    end

end




# def_enum *JapaneseGame::Actors


class TestJG < Test::Unit::TestCase

    def setup
        @game = JapaneseGame.new
        @bank1= JapaneseGamePosition::BANK1
        @boat = JapaneseGamePosition::BOAT
    end
    
    def test_check
        test_cases = [
            [ true,  [Mother]  ],
            [ false, [Mother, Son1]  ],
            [ false, [Mother, Son2]  ],
            [ false, [Mother, Son2, Policeman]  ],
            [ true,  [Father, Son1, Mother]  ],
            [ true,  [Father, Son2, Mother]  ],
            
            [ true,  [Father]  ],
            [ false, [Father, Daughter1]  ],
            [ false, [Father, Daughter2]  ],
            [ false, [Father, Daughter2, Policeman]  ],
            [ true,  [Father, Daughter1, Mother]  ],
            [ true,  [Father, Daughter2, Mother]  ],
            [ true,  [Father, Son1, Daughter2, Mother]  ],
            
            [ true,  [Robber]  ],
            [ true,  [Policeman, Robber]  ],
            [ true,  [Robber, Policeman]  ],
            [ true,  [Policeman, Robber, Mother]  ],
            [ true,  [Policeman, Robber, Mother]  ],
            [ true,  [Policeman, Robber, Father]  ],
            [ true,  [Policeman, Robber, Mother, Father, Son1]  ],
            [ false, [Robber, Mother]  ],
            [ false, [Robber, Father]  ],
            [ false, [Robber, Son1]  ],
            [ false, [Robber, Daughter2]  ],
            
        ]
        
        test_cases.each{|expected, actors|
            assert_equal expected, @game.check(actors), actors.inspect
        }
    end
    
    def test_check_boat
        test_cases = [
            [ true,  [Mother]  ],
            [ true,  [Father]  ],
            [ true,  [Policeman]],
            
            [ true,  [Mother, Father]  ],
            [ true,  [Mother, Policeman]  ],
            [ true,  [Mother, Son1]  ],
            [ true,  [Mother, Daughter2]  ],
            [ true,  [Policeman, Robber]],
            
            [ false, [Son2, Son1]  ],
            [ false, [Daughter2, Son2]  ],
            [ false, [Robber]  ],
            [ false, [Son1]  ],
        ]
        
        test_cases.each{|expected, actors|
            assert_equal expected, @game.check_boat(actors), actors.inspect
        }
    end
    
    def test_collect_moves
        pos = JapaneseGamePosition[ [Policeman],[],[] ]
        moves = @game.collect_moves( pos, @bank1 )
        assert moves.include?( [Policeman] )
        assert_equal 1, moves.size
        assert_equal [[Policeman]], moves
        
        pos = JapaneseGamePosition[ [Policeman, Robber],[],[] ]
        moves = @game.collect_moves( pos, @bank1 )
        assert moves.include?( [Policeman, Robber] )
        assert moves.include?( [Policeman] )
        assert_equal 2, moves.size
        
        pos = JapaneseGamePosition[ [Policeman, Robber, Daughter1],[],[] ]
        moves = @game.collect_moves( pos, @bank1 )
        assert_equal 3, moves.size
        assert moves.include?( [Policeman, Robber] )
        assert moves.include?( [Daughter1, Policeman] )
        assert moves.include?( [Policeman] )
        
        pos = JapaneseGamePosition[ [Policeman, Robber, Daughter1, Father],[],[] ]
        moves = @game.collect_moves( pos, @bank1 )
        # 2 single moves (Policeman only, Father only), 
        # 5 pair moves (Policeman and/or Father with one other actor)
        assert_equal 7, moves.size

    end
    
    def test_visited
        assert_equal [],        @game.visited
        assert_equal false,     @game.visited?( 1 )
        assert_equal [1],       @game.visited
        assert_equal true,      @game.visited?( 1 )
        assert_equal false,     @game.visited?( 2 )
        assert_equal [1,2],     @game.visited
        
        
    end
    
end


