Skip to content

Commit

Permalink
feat(#121): Implement power-ups (#132)
Browse files Browse the repository at this point in the history
These changes will include:

- A way for receptors to light up in order to indicate to the user a power up can be obtained for the receptor
- Allow the user to activate the power up after the receptor has been marked, which includes
    - Add bonus points to the score of the user
    - Spawn 2 joker balls in a row

Closes #121
  • Loading branch information
mrijm authored and fabianishere committed Oct 20, 2017
1 parent 063ea76 commit 682fc3d
Show file tree
Hide file tree
Showing 33 changed files with 794 additions and 199 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package nl.tudelft.broccoli.core;


import nl.tudelft.broccoli.core.config.IntegerProperty;
import nl.tudelft.broccoli.core.config.Property;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
package nl.tudelft.broccoli.core.level;

import nl.tudelft.broccoli.core.MarbleType;
import nl.tudelft.broccoli.core.config.Configuration;
import nl.tudelft.broccoli.core.grid.Grid;
import nl.tudelft.broccoli.core.nexus.NexusContext;
import nl.tudelft.broccoli.core.nexus.SpawningNexus;
import nl.tudelft.broccoli.core.powerup.PowerUp;
import nl.tudelft.broccoli.core.powerup.PowerUpFactory;
import nl.tudelft.broccoli.core.powerup.RandomPowerUpFactory;
import nl.tudelft.broccoli.core.powerup.bonus.BonusPowerUpFactory;
import nl.tudelft.broccoli.core.powerup.joker.JokerPowerUpFactory;
import nl.tudelft.broccoli.core.receptor.Receptor;

import java.util.*;
import java.util.stream.Collectors;

/**
* An abstract {@link GameSession} class which already implements some of the methods for the user.
Expand All @@ -24,6 +36,16 @@ public abstract class AbstractGameSession implements GameSession {
*/
private final Progress progress;

/**
* The {@link NexusContext} instance.
*/
private final NexusContext nexusContext;

/**
* The {@link PowerUpFactory} to use.
*/
private final PowerUpFactory powerUpFactory;

/**
* Construct a {@link AbstractGameSession} instance.
*
Expand All @@ -33,6 +55,26 @@ public AbstractGameSession(Configuration config) {
this.config = config;
this.grid = new Grid(this, config.get(Grid.WIDTH), config.get(Grid.HEIGHT));
this.progress = new Progress(grid);

// Read the initial sequence of balls from the configuration
List<String> initial = config.get(SpawningNexus.INITIAL_SEQUENCE);
Queue<MarbleType> queue = new ArrayDeque<>(initial.stream()
.map(str -> {
try {
return MarbleType.valueOf(str);
} catch (IllegalArgumentException e) {
return MarbleType.BLUE;
}
}).collect(Collectors.toList())
);

this.nexusContext = new NexusContext(queue, new Random(),
config.get(SpawningNexus.JOKER_PROBABILITY));

NavigableMap<Double, PowerUpFactory> cdf = new TreeMap<>();
cdf.put(0.0, new BonusPowerUpFactory());
cdf.put(0.8, new JokerPowerUpFactory());
this.powerUpFactory = new RandomPowerUpFactory(new Random(), cdf);
}

/**
Expand Down Expand Up @@ -63,5 +105,26 @@ public Grid getGrid() {
public Progress getProgress() {
return progress;
}

/**
* Return the {@link NexusContext} of the game session.
*
* @return The context of the nexus instances.
*/
@Override
public NexusContext getNexusContext() {
return nexusContext;
}

/**
* Return the {@link PowerUpFactory} used by this {@link GameSession} to create new
* {@link PowerUp}s to be assigned to {@link Receptor}s.
*
* @return The power-up factory of the game session.
*/
@Override
public PowerUpFactory getPowerUpFactory() {
return powerUpFactory;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,10 @@
import nl.tudelft.broccoli.core.grid.Direction;
import nl.tudelft.broccoli.core.grid.Grid;
import nl.tudelft.broccoli.core.nexus.Nexus;
import nl.tudelft.broccoli.core.nexus.NexusContext;
import nl.tudelft.broccoli.core.nexus.SpawningNexus;
import nl.tudelft.broccoli.core.receptor.Receptor;
import nl.tudelft.broccoli.core.track.*;

import java.util.ArrayDeque;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.stream.Collectors;

/**
* A very basic, static {@link Level} used for testing purposes.
*
Expand Down Expand Up @@ -59,6 +52,7 @@ public Difficulty getDifficulty() {
* A very basic, static {@link GameSession} created by {@link EasyLevel} instances.
*/
private class EasyGame extends AbstractGameSession {

/**
* Construct a {@link EasyGame} instance.
*
Expand All @@ -68,25 +62,10 @@ public EasyGame(Configuration config) {
super(config);
Grid grid = getGrid();

NexusContext context = new NexusContext();
grid.place(0, 3, new Nexus(context));
grid.place(1, 3, new Nexus(context));
grid.place(2, 3, new Nexus(context));

// Read the initial sequence of balls from the configuration
List<String> initialStrings = config.get(SpawningNexus.INITIAL_SEQUENCE);
Queue<MarbleType> initial = new ArrayDeque<>(initialStrings.stream()
.map(str -> {
try {
return MarbleType.valueOf(str);
} catch (IllegalArgumentException e) {
return MarbleType.BLUE;
}
}).collect(Collectors.toList())
);

grid.place(3, 3, new SpawningNexus(context, new Random(), Direction.RIGHT,
config.get(SpawningNexus.JOKER_PROBABILITY), initial));
grid.place(0, 3, new Nexus(getNexusContext()));
grid.place(1, 3, new Nexus(getNexusContext()));
grid.place(2, 3, new Nexus(getNexusContext()));
grid.place(3, 3, new SpawningNexus(getNexusContext(), Direction.RIGHT));

TimerTile timer = new TimerTile(config.get(TimerTile.MAX_TIME));
grid.place(3, 2, timer);
Expand All @@ -104,7 +83,6 @@ public EasyGame(Configuration config) {
grid.place(2, 0, new Receptor());
}


/**
* Return the {@link Level} this game represents.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
package nl.tudelft.broccoli.core.level;

import nl.tudelft.broccoli.core.grid.Grid;
import nl.tudelft.broccoli.core.nexus.NexusContext;
import nl.tudelft.broccoli.core.powerup.PowerUp;
import nl.tudelft.broccoli.core.powerup.PowerUpFactory;
import nl.tudelft.broccoli.core.receptor.Receptor;

/**
* A single playing session of a Gudeballs game.
Expand Down Expand Up @@ -55,4 +59,18 @@ public interface GameSession {
*/
Level getLevel();

/**
* Return the {@link NexusContext} of the game session.
*
* @return The context of the nexus instances.
*/
NexusContext getNexusContext();

/**
* Return the {@link PowerUpFactory} used by this {@link GameSession} to create new
* {@link PowerUp}s to be assigned to {@link Receptor}s.
*
* @return The power-up factory of the game session.
*/
PowerUpFactory getPowerUpFactory();
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package nl.tudelft.broccoli.core.level;

import nl.tudelft.broccoli.core.MarbleType;
import nl.tudelft.broccoli.core.TimerTile;
import nl.tudelft.broccoli.core.config.Configuration;
import nl.tudelft.broccoli.core.grid.Direction;
Expand All @@ -12,12 +11,6 @@
import nl.tudelft.broccoli.core.track.HorizontalTrack;
import nl.tudelft.broccoli.core.track.VerticalTrack;

import java.util.ArrayDeque;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.stream.Collectors;

/**
* A very basic, static {@link Level} used for testing purposes.
*
Expand Down Expand Up @@ -70,25 +63,11 @@ public HardGame(Configuration config) {
super(config);
Grid grid = getGrid();

NexusContext context = new NexusContext();
NexusContext context = getNexusContext();
grid.place(0, 3, new Nexus(context));
grid.place(1, 3, new Nexus(context));
grid.place(2, 3, new Nexus(context));

// Read the initial sequence of balls from the configuration
List<String> initialStrings = config.get(SpawningNexus.INITIAL_SEQUENCE);
Queue<MarbleType> initial = new ArrayDeque<>(initialStrings.stream()
.map(str -> {
try {
return MarbleType.valueOf(str);
} catch (IllegalArgumentException e) {
return MarbleType.BLUE;
}
}).collect(Collectors.toList())
);

grid.place(3, 3, new SpawningNexus(context, new Random(), Direction.RIGHT,
config.get(SpawningNexus.JOKER_PROBABILITY), initial));
grid.place(3, 3, new SpawningNexus(context, Direction.RIGHT));

TimerTile timer = new TimerTile(config.get(TimerTile.MAX_TIME));
grid.place(3, 2, timer);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package nl.tudelft.broccoli.core.level;

import nl.tudelft.broccoli.core.MarbleType;
import nl.tudelft.broccoli.core.TimerTile;
import nl.tudelft.broccoli.core.config.Configuration;
import nl.tudelft.broccoli.core.grid.Direction;
Expand All @@ -12,12 +11,6 @@
import nl.tudelft.broccoli.core.track.HorizontalTrack;
import nl.tudelft.broccoli.core.track.VerticalTrack;

import java.util.ArrayDeque;
import java.util.List;
import java.util.Queue;
import java.util.Random;
import java.util.stream.Collectors;

/**
* A very basic, static {@link Level} used for testing purposes.
*
Expand Down Expand Up @@ -70,25 +63,11 @@ public MediumGame(Configuration config) {
super(config);
Grid grid = getGrid();

NexusContext context = new NexusContext();
NexusContext context = getNexusContext();
grid.place(0, 3, new Nexus(context));
grid.place(1, 3, new Nexus(context));
grid.place(2, 3, new Nexus(context));

// Read the initial sequence of balls from the configuration
List<String> initialStrings = config.get(SpawningNexus.INITIAL_SEQUENCE);
Queue<MarbleType> initial = new ArrayDeque<>(initialStrings.stream()
.map(str -> {
try {
return MarbleType.valueOf(str);
} catch (IllegalArgumentException e) {
return MarbleType.BLUE;
}
}).collect(Collectors.toList())
);

grid.place(3, 3, new SpawningNexus(context, new Random(), Direction.RIGHT,
config.get(SpawningNexus.JOKER_PROBABILITY), initial));
grid.place(3, 3, new SpawningNexus(context, Direction.RIGHT));

TimerTile timer = new TimerTile(config.get(TimerTile.MAX_TIME));
grid.place(3, 2, timer);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,109 @@

package nl.tudelft.broccoli.core.nexus;

import nl.tudelft.broccoli.core.Marble;
import nl.tudelft.broccoli.core.MarbleType;

import java.util.Arrays;
import java.util.Queue;
import java.util.Random;

/**
* A context for {@link Nexus} instances to check the state of other parts.
*
* @author Earth Grob (w.lauwapong@student.tudelft.nl)
* @author Fabian Mastenbroek (f.s.mastenbroek@student.tudelft.nl)
*/
public class NexusContext {
/**
* The possible type of marbles.
*/
private static final MarbleType[] MARBLES = MarbleType.values();

/**
* A flag to indicate the nexus is currently occupied by a ball.
*/
private boolean occupied;

/**
* The remaining sequence of balls to spawn.
*/
private final Queue<MarbleType> queue;

/**
* The {@link Random} instance used for determining the color of the spawned ball.
*/
private final Random random;

/**
* The probability of a joker occurring.
*/
private final double joker;

/**
* Construct a {@link NexusContext} instance.
*
* @param queue The queue of balls to spawn.
* @param random The random number generator to use for selecting the colors.
* @param joker The probability that a joker is generated.
*/
public NexusContext(Queue<MarbleType> queue, Random random, double joker) {
this.queue = queue;
this.random = random;
this.joker = joker;

// Generate the first ball to be spawned if the queue is empty initially.
if (queue.isEmpty()) {
queue.add(generate());
}
}

/**
* Peek into the queue of marbles to be spawned, without removing the element from the queue.
*
* @return The type of the marble to spawn next.
*/
public MarbleType peek() {
return queue.peek();
}

/**
* Poll into the queue of marbles to be spawned, removing the element from the queue.
*
* @return The type of the marble to spawn next.
*/
public MarbleType poll() {
MarbleType type = queue.poll();

if (queue.isEmpty()) {
queue.add(generate());
}

return type;
}

/**
* Add the given {@link MarbleType}s to the queue of marbles to be spawned.
*
* @param types The marble types to add.
*/
public void add(MarbleType ...types) {
queue.addAll(Arrays.asList(types));
}

/**
* Generate a random {@link Marble} to be spawned by this {@link SpawningNexus}.
*
* @return The marble to be spawned.
*/
private MarbleType generate() {
if (random.nextDouble() < joker) {
return MarbleType.JOKER;
}

return MARBLES[random.nextInt(MARBLES.length - 1)];
}

/**
* Determine whether a part of the nexus is occupied by a ball.
*
Expand Down
Loading

0 comments on commit 682fc3d

Please sign in to comment.