package rabbitescape.engine.logic; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.CoreMatchers.nullValue; import static org.hamcrest.MatcherAssert.assertThat; import static rabbitescape.engine.ChangeDescription.State.RABBIT_BRIDGING_RIGHT_1; import static rabbitescape.engine.ChangeDescription.State.RABBIT_WALKING_RIGHT; import static rabbitescape.engine.Tools.equalTo; import static rabbitescape.engine.World.CompletionState.LOST; import static rabbitescape.engine.World.CompletionState.RUNNING; import static rabbitescape.engine.World.CompletionState.WON; import static rabbitescape.engine.textworld.TextWorldManip.createWorld; import static rabbitescape.engine.util.Util.range; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.junit.Test; import rabbitescape.engine.Rabbit; import rabbitescape.engine.Token; import rabbitescape.engine.World; import rabbitescape.engine.World.DontStepAfterFinish; import rabbitescape.engine.WorldStatsListener; import rabbitescape.engine.textworld.TextWorldManip; import rabbitescape.engine.util.Position; import rabbitescape.engine.util.WaterUtil; public class TestWorld { @Test public void World_will_not_step_with_no_live_rabbits() { World world = createWorld( ":num_rabbits=1", " Q ", " O ", "#####" ); world.step(); world.step(); world.step(); world.step(); // All gone now, so // This step should throw DontStepAfterFinish exception = null; try { world.step(); } catch( DontStepAfterFinish e ) { exception = e; } assertThat( exception, notNullValue() ); } @Test public void World_reports_when_finished_no_live_rabbits() { World world = createWorld( ":num_rabbits=5", ":rabbit_delay=5", " Q ", " O ", // Exit right below entrance "###" ); world.step(); // First one over the exit fiveSteps( world ); assertThat( world.completionState(), equalTo( RUNNING ) ); fiveSteps( world ); assertThat( world.completionState(), equalTo( RUNNING ) ); fiveSteps( world ); assertThat( world.completionState(), equalTo( RUNNING ) ); fiveSteps( world ); assertThat( world.completionState(), equalTo( RUNNING ) ); // Fifth one over the exit: send it in world.step(); // We should now be finished assertThat( world.completionState(), equalTo( WON ) ); } @Test public void World_reports_won_when_enough_rabbits_saved() { World world = createWorld( ":num_rabbits=0", ":num_saved=2", ":num_to_save=2", " ", "###" ); // We should now be finished assertThat( world.completionState(), equalTo( WON ) ); } @Test public void World_reports_lost_when_not_enough_rabbits_saved() { World world = createWorld( ":num_rabbits=0", ":num_saved=2", ":num_to_save=3", " ", "###" ); // We should now be finished assertThat( world.completionState(), equalTo( LOST ) ); } @Test public void World_is_in_running_status_at_the_start() { World world = createWorld( ":num_rabbits=3", ":num_saved=2", ":num_to_save=3", " ", "###" ); assertThat( world.completionState(), equalTo( RUNNING ) ); } @Test public void World_stores_number_of_abilities() { World world = createWorld( ":bash=5", " ", "###" ); assertThat( world.abilities.get( Token.Type.bash ), equalTo( 5 ) ); } @Test public void World_reduces_abilities_when_you_use_a_token() { World world = createWorld( ":bash=5", ":dig=3", ":bridge=2", " ", "###" ); // This is what we are testing world.changes.addToken( 0, 0, Token.Type.bash ); world.step(); // There should be one less bash assertThat( world.abilities.get( Token.Type.bash ), equalTo( 4 ) ); // The dig ability was unaffected assertThat( world.abilities.get( Token.Type.dig ), equalTo( 3 ) ); // The bridge ability was unaffected assertThat( world.abilities.get( Token.Type.bridge ), equalTo( 2 ) ); } @Test public void World_refuses_to_add_a_token_if_none_left() { World world = createWorld( ":bash=1", " ", "###" ); // Use up the last bash world.changes.addToken( 0, 0, Token.Type.bash ); world.step(); // Sanity assertThat( world.abilities.get( Token.Type.bash ), equalTo( 0 ) ); // This is what we are testing: can't add another World.UnableToAddToken caughtException = null; try { world.changes.addToken( 1, 0, Token.Type.bash ); } catch ( World.UnableToAddToken e ) { caughtException = e; } assertThat( caughtException, notNullValue() ); } @Test public void Cant_find_token_if_already_removed() { World world = createWorld( " i ", "###" ); Token token = world.getTokenAt( 1, 0 ); // Sanity assertThat( token, is( notNullValue() ) ); // Remove it world.changes.removeToken( token ); // This is what we are testing: it's gone assertThat( world.getTokenAt( 1, 0 ), is( nullValue() ) ); // Sanity: after a step it's still gone world.step(); assertThat( world.getTokenAt( 1, 0 ), is( nullValue() ) ); } @Test public void Can_find_token_if_there_were_2_and_only_1_is_removed() { World world = createWorld( " i ", "###" ); world.things.add( new Token( 1, 0, Token.Type.bridge ) ); Token token = world.getTokenAt( 1, 0 ); // Sanity assertThat( token, is( notNullValue() ) ); // Remove one token world.changes.removeToken( token ); // This is what we are testing: there's another Token token2 = world.getTokenAt( 1, 0 ); assertThat( token2, is( notNullValue() ) ); // Remove that one too world.changes.removeToken( token2 ); // Now there's nothing left assertThat( world.getTokenAt( 1, 0 ), is( nullValue() ) ); } @Test public void No_rabbits_at_a_location_gives_empty_array() { World world = createWorld( "*i ", "###", ":*=rr" // 2 rabbits in the same place ); world.step(); // Now 1 is a bridger // This is what we are testing: there are no rabbits in the empty square Rabbit[] rabbits = world.getRabbitsAt( 2, 0 ); assertThat( rabbits.length, equalTo( 0 ) ); } @Test public void Can_find_all_rabbits_at_a_location() { World world = createWorld( "*i ", "###", ":*=rr" // 2 rabbits in the same place ); world.step(); // Now 1 is a bridger // This is what we are testing: ask what's in the rabbitty square Rabbit[] rabbits = world.getRabbitsAt( 1, 0 ); assertThat( rabbits[0].state, equalTo( RABBIT_BRIDGING_RIGHT_1 ) ); assertThat( rabbits[1].state, equalTo( RABBIT_WALKING_RIGHT ) ); assertThat( rabbits.length, equalTo( 2 ) ); } @Test public void Explode_all_rabbits_explodes_all_rabbits() { World world = createWorld( "#r#r#r#r#r#", "###########" ); world.step(); // Sanity: 5 rabbits alive assertThat( world.rabbits.size(), equalTo( 5 ) ); // This is what we are testing: explode them all world.changes.explodeAllRabbits(); world.step(); // They are exploding assertThat( TextWorldManip.renderWorld( world, true, false ), equalTo( "#P#P#P#P#P#", "###########" ) ); // And after another time step... world.step(); // ... they are dead assertThat( world.rabbits.size(), equalTo( 0 ) ); } @Test public void We_are_notified_when_rabbits_are_saved() { class TrackingWorldStatsListener implements WorldStatsListener { class Call { public int num_saved; public int num_to_save; } public List<Call> calls = new ArrayList<Call>(); @Override public void worldStats( int num_saved, int num_to_save ) { Call call = new Call(); call.num_saved = num_saved; call.num_to_save = num_to_save; calls.add( call ); } } TrackingWorldStatsListener statsListener = new TrackingWorldStatsListener(); World world = createWorld( statsListener, ":num_to_save=7", "r O", "###" ); // Sanity - we provide stats when we create the world assertThat( statsListener.calls.size(), equalTo( 1 ) ); assertThat( statsListener.calls.get( 0 ).num_saved, equalTo( 0 ) ); assertThat( statsListener.calls.get( 0 ).num_to_save, equalTo( 7 ) ); world.step(); // Rabbit moved assertThat( statsListener.calls.size(), equalTo( 1 ) ); world.step(); // Rabbit entering exit assertThat( statsListener.calls.size(), equalTo( 1 ) ); world.step(); // Rabbit has entered exit assertThat( statsListener.calls.size(), equalTo( 2 ) ); assertThat( statsListener.calls.get( 1 ).num_saved, equalTo( 1 ) ); assertThat( statsListener.calls.get( 1 ).num_to_save, equalTo( 7 ) ); } @Test public void Water_contents_can_be_retrieved() { World world = createWorld( "#N#n# #", "#######" ); Map<Position, Integer> waterContents = world.getWaterContents(); assertThat( waterContents.get( new Position( 1, 0 ) ), equalTo( WaterUtil.MAX_CAPACITY ) ); assertThat( waterContents.get( new Position( 3, 0 ) ), equalTo( WaterUtil.HALF_CAPACITY ) ); // There should be no reference to the empty region. assertThat( waterContents.containsKey( new Position( 5, 0 ) ), equalTo( false ) ); } // --- private void fiveSteps( World world ) { for( @SuppressWarnings( "unused" ) int t : range( 5 ) ) { world.step(); } } }