package rabbitescape.engine.util; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.MatcherAssert.assertThat; import static rabbitescape.engine.CellularDirection.DOWN; import static rabbitescape.engine.CellularDirection.HERE; import static rabbitescape.engine.CellularDirection.LEFT; import static rabbitescape.engine.CellularDirection.RIGHT; import static rabbitescape.engine.CellularDirection.UP; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.Test; import rabbitescape.engine.CellularDirection; import rabbitescape.engine.WaterRegion; public class TestWaterUtil { @Test public void empty_neighbourhood() { // Create a water table containing just one region. WaterRegion region = new WaterRegion( 0, 0, Util.newSet( UP, LEFT, RIGHT, DOWN ), 100 ); LookupTable2D<WaterRegion> waterTable = new LookupTable2D<>( Arrays.asList( region ), new Dimension( 1, 1 ) ); Map<CellularDirection, WaterRegion> neighbourhood = WaterUtil.findNeighbourhood( region , waterTable ); assertThat( neighbourhood.keySet(), equalTo( Util.newSet( HERE ) ) ); assertThat( neighbourhood.get( HERE ), equalTo( region ) ); } @Test public void neighbourhood_includes_perimeter() { // Create a 1x1 water table with a perimeter of size 1 all the way around. List<WaterRegion> allRegions = new ArrayList<>(); for ( int x = -1; x <= 1; x++ ) { for ( int y = -1; y <= 1; y++ ) { allRegions.add( new WaterRegion( x, y, Util.newSet( UP, LEFT, RIGHT, DOWN ), 100 ) ); } } LookupTable2D<WaterRegion> waterTable = new LookupTable2D<>( allRegions, new Dimension( 1, 1 ) ); WaterRegion region = waterTable.getItemAt( 0, 0 ); Map<CellularDirection, WaterRegion> neighbourhood = WaterUtil.findNeighbourhood( region , waterTable ); assertThat( neighbourhood.keySet(), equalTo( Util.newSet( UP, LEFT, RIGHT, DOWN, HERE ) ) ); } /** * Calculate the neighbourhood of a ramp in a water table of the form: * <pre> * [/RAMP] * [SPACE][/RAMP][BLOCK] * [SPACE] * </pre> * Check that the only neighbour returned is the space to the left. */ @Test public void connection_has_to_be_in_both_directions() { WaterRegion middle = new WaterRegion( 1, 1, Util.newSet( UP, LEFT ), 10 ); WaterRegion top = new WaterRegion( 1, 0, Util.newSet( UP, LEFT ), 20 ); WaterRegion left = new WaterRegion( 0, 1, Util.newSet( UP, LEFT, RIGHT, DOWN ), 30 ); // Right has no WaterRegion, because it's a solid block. WaterRegion bottom = new WaterRegion( 1, 2, Util.newSet( UP, LEFT, RIGHT, DOWN ), 40 ); LookupTable2D<WaterRegion> waterTable = new LookupTable2D<>( Arrays.asList( middle, top, left, bottom ), new Dimension( 3, 3 ) ); Map<CellularDirection, WaterRegion> neighbourhood = WaterUtil.findNeighbourhood( middle , waterTable ); assertThat( neighbourhood.keySet(), equalTo( Util.newSet( LEFT, HERE ) ) ); assertThat( neighbourhood.get( LEFT ), equalTo( left ) ); assertThat( neighbourhood.get( HERE ), equalTo( middle ) ); } @Test( expected = IllegalStateException.class ) public void exception_if_two_regions_connected_on_same_side() { WaterRegion region = new WaterRegion( 0, 0, Util.newSet( DOWN ), 100 ); WaterRegion bottomA = new WaterRegion( 0, 1, Util.newSet( UP ), 100 ); WaterRegion bottomB = new WaterRegion( 0, 1, Util.newSet( UP ), 100 ); LookupTable2D<WaterRegion> waterTable = new LookupTable2D<>( Arrays.asList( region, bottomA, bottomB ), new Dimension( 2, 2 ) ); WaterUtil.findNeighbourhood( region , waterTable ); } @Test public void no_exception_if_two_regions_on_same_side_arent_both_connected() { WaterRegion region = new WaterRegion( 0, 0, Util.newSet( DOWN ), 100 ); WaterRegion bottomA = new WaterRegion( 0, 1, Util.newSet( UP ), 100 ); WaterRegion bottomB = new WaterRegion( 0, 1, Util.newSet( LEFT ), 100 ); LookupTable2D<WaterRegion> waterTable = new LookupTable2D<>( Arrays.asList( region, bottomA, bottomB ), new Dimension( 2, 2 ) ); Map<CellularDirection, WaterRegion> neighbourhood = WaterUtil.findNeighbourhood( region , waterTable ); assertThat( neighbourhood.keySet(), equalTo( Util.newSet( DOWN, HERE ) ) ); assertThat( neighbourhood.get( DOWN ), equalTo( bottomA ) ); assertThat( neighbourhood.get( HERE ), equalTo( region ) ); } @Test public void water_falls_down_if_space_below() { Map<CellularDirection, WaterRegion> neighbourhood = new HashMap<>(); neighbourhood.put( HERE, new WaterRegion( 0, 0, null, 100, 100, false ) ); neighbourhood.put( DOWN, new WaterRegion( 0, 0, null, 100, 0, false ) ); Map<CellularDirection, Integer> flow = WaterUtil.calculateFlow( neighbourhood ); assertThat( flow.get( DOWN ), equalTo( 100 ) ); } @Test public void water_stays_if_no_space_below() { Map<CellularDirection, WaterRegion> neighbourhood = new HashMap<>(); neighbourhood.put( HERE, new WaterRegion( 0, 0, null, 100, 100, false ) ); neighbourhood.put( DOWN, new WaterRegion( 0, 0, null, 100, 100, false ) ); Map<CellularDirection, Integer> flow = WaterUtil.calculateFlow( neighbourhood ); assertThat( "A small amount of water should fall due to compression.", flow.get( DOWN ), equalTo( 20 ) ); } @Test public void water_spreads_on_a_surface() { Map<CellularDirection, WaterRegion> neighbourhood = new HashMap<>(); neighbourhood.put( HERE, new WaterRegion( 0, 0, null, 100, 100, false ) ); neighbourhood.put( LEFT, new WaterRegion( 0, 0, null, 100, 0, false ) ); neighbourhood.put( RIGHT, new WaterRegion( 0, 0, null, 100, 0, false ) ); Map<CellularDirection, Integer> flow = WaterUtil.calculateFlow( neighbourhood ); assertThat( "No water should fall as there's no region there.", flow.get( DOWN ), equalTo( 0 ) ); assertThat( flow.get( LEFT ), equalTo( 33 ) ); assertThat( flow.get( RIGHT ), equalTo( 33 ) ); } @Test public void water_spreads_according_to_existing_contents() { Map<CellularDirection, WaterRegion> neighbourhood = new HashMap<>(); neighbourhood.put( HERE, new WaterRegion( 0, 0, null, 100, 6, false ) ); neighbourhood.put( LEFT, new WaterRegion( 0, 0, null, 100, 2, false ) ); neighbourhood.put( RIGHT, new WaterRegion( 0, 0, null, 100, 1, false ) ); Map<CellularDirection, Integer> flow = WaterUtil.calculateFlow( neighbourhood ); // To balance the three cells we need to end up with 3 units in each cell. assertThat( flow.get( LEFT ), equalTo( 1 ) ); assertThat( flow.get( RIGHT ), equalTo( 2 ) ); } @Test public void water_doesnt_spread_if_neighbours_are_fuller() { Map<CellularDirection, WaterRegion> neighbourhood = new HashMap<>(); neighbourhood.put( HERE, new WaterRegion( 0, 0, null, 100, 99, false ) ); neighbourhood.put( LEFT, new WaterRegion( 0, 0, null, 100, 101, false ) ); neighbourhood.put( RIGHT, new WaterRegion( 0, 0, null, 100, 100, false ) ); Map<CellularDirection, Integer> flow = WaterUtil.calculateFlow( neighbourhood ); // To balance the three cells we need to end up with 3 units in each cell. assertThat( flow.get( LEFT ), equalTo( 0 ) ); assertThat( flow.get( RIGHT ), equalTo( 0 ) ); } @Test public void pressurised_water_flows_upwards() { Map<CellularDirection, WaterRegion> neighbourhood = new HashMap<>(); neighbourhood.put( HERE, new WaterRegion( 0, 0, null, 100, 200, false ) ); neighbourhood.put( UP, new WaterRegion( 0, 0, null, 100, 0, false ) ); Map<CellularDirection, Integer> flow = WaterUtil.calculateFlow( neighbourhood ); // 68 seems a fairly arbitrary number. It's calculated using the MAGIC_UP_NUMERATOR and MAGIC_UP_DENOMINATOR. assertThat( flow.get( UP ), equalTo( 68 ) ); } @Test public void pressurised_water_can_flow_all_ways_at_once() { Map<CellularDirection, WaterRegion> neighbourhood = new HashMap<>(); neighbourhood.put( HERE, new WaterRegion( 0, 0, null, 10, 200, false ) ); neighbourhood.put( UP, new WaterRegion( 0, 0, null, 10, 0, false ) ); neighbourhood.put( LEFT, new WaterRegion( 0, 0, null, 10, 0, false ) ); neighbourhood.put( RIGHT, new WaterRegion( 0, 0, null, 10, 0, false ) ); neighbourhood.put( DOWN, new WaterRegion( 0, 0, null, 10, 0, false ) ); Map<CellularDirection, Integer> flow = WaterUtil.calculateFlow( neighbourhood ); assertThat( flow.get( UP ), equalTo( 62 ) ); assertThat( flow.get( LEFT ), equalTo( 27 ) ); assertThat( flow.get( RIGHT ), equalTo( 27 ) ); assertThat( flow.get( DOWN ), equalTo( 47 ) ); // This will leaving 37 units unmoved. } }