import java.util.Arrays; import java.util.HashMap; import java.util.Map; import java.util.Scanner; import java.util.regex.Pattern; import java.util.stream.Stream; public class Jeu { static interface ThenThat { public void run(Player player); } static class ChainAction implements Action { final Action ifthis; ThenThat thenthat; public ChainAction(Action ifthis) { this.ifthis = ifthis; } public ChainAction then(ThenThat thenthat) { this.thenthat = thenthat; return new ChainAction(this); } @Override public boolean match(String input, Player player) { if (ifthis.match(input, player)) { if (thenthat != null) { thenthat.run(player); } return true; } return false; } } static ThenThat say(String reaction) { return p -> System.out.println(reaction); } static ChainAction getRegexpMatcher(String regexp) { Pattern p = Pattern.compile(regexp); return new ChainAction((input, player) -> { return p.matcher(input).matches(); }); } static ChainAction getVOMatcher(String verb, String object) { return getRegexpMatcher(verb + "( the)? " + object); } static ChainAction getVOReaction(String verb, String object, String reaction) { return getVOMatcher(verb, object).then(say(reaction)); } static class StateMachineAction implements Action { final Action matcher; final Map reactions = new HashMap<>(); public int state = 0; public StateMachineAction(Action matcher) { this.matcher = matcher; } public StateMachineAction when(int state, ThenThat thenthat) { this.reactions.put(state, thenthat); return this; } @Override public boolean match(String input, Player player) { if (!this.matcher.match(input, player)) { return false; } if (this.reactions.containsKey(this.state)) { this.reactions.get(this.state).run(player); } return true; } } static class OnceAction extends StateMachineAction { public OnceAction(Action matcher) { super(matcher); } public OnceAction atFirst(ThenThat thenThat) { this.when(0, player -> { thenThat.run(player); this.state = 1; }); return this; } public OnceAction thenLater(ThenThat thenThat) { this.when(1, thenThat); return this; } } static Place makeWorld() { Place startingPoint = new Place("Some sort of place"); startingPoint.description = "You find yourself in MadVillain's lair.\n" + "It's reaaally dark here. You can feel a light switch on the wall on your right."; Place hallWay = new Place("Hallway"); hallWay.description = "This is the hallway that you came from. You don't spend much time looking at it\n" + "so I won't spend much time describing it. It leads *outside*. Fresh air."; Place outside = new Place("outside"); outside.description = "The great outdoors. I mean, it's still the city. Dull gray and mornful city.\n" + "But it's also outdoors. And great, in a way. You can go anywhere. If you can."; outside.actions.add(getVOReaction("go to", "hallway", "There's nothing to do there anymore.")); hallWay.actions.add(getRegexpMatcher("(go|get) (out|outside)").then(player -> { System.out.println("The bright light of an overcast mid-morning in the city blinds you for a few seconds.\n" + "It's not actually very bright. The smog absorbs a sizeable portion of the meager apricity."); player.place = outside; })); final Action takeNotebook = new OnceAction(getVOMatcher("take", "notebook")) .atFirst(player -> { System.out.println("You reach out and take the notebook. It's not very clean."); final Item notebook = new Item("notebook"); final Action read = new OnceAction(getVOMatcher("read", "notebook")) .atFirst(say("It's full of gibberish and satanic doodles. You feel sullied.")) .thenLater(say("No way you open this book again.")); notebook.action = read; player.items.add(notebook); startingPoint.description = "An extremely banal room. There's nothing here.\n" + "I mean there is NOTHING here.\n" + "Except the door behind you. You know, the DOOR."; }).thenLater(say("It's already in your pocket!")); final Action openDoor = getVOMatcher("open", "door").then(player -> { if (player.items.stream().anyMatch(i -> i.name.equals("notebook"))) { System.out.println("Having spent enough time in this sordid place, you turn on your heels and head out."); player.place = hallWay; } else { System.out.println("Ahem. I said there is a NOTEBOOK here."); } }); final Action flipSwitch = new OnceAction( getRegexpMatcher("(turn ((the )?lights? on|on( the)? lights?)|flip( the)?( light)? switch)")) .atFirst(player -> { System.out.println("*clic*"); startingPoint.description = "An extremely banal room. There's nothing here.\n" + "I mean, there's nothing here, aside from a weathered notebook.\n" + "There's also a door, just behind you."; startingPoint.actions.add(takeNotebook); startingPoint.actions.add(openDoor); }) .thenLater(say("It's an LED bulb, so it's ok to leave the light on.")); startingPoint.actions.add(flipSwitch); return startingPoint; } static boolean match(Action[] actions, String input, Player player) { for (final Action action : actions) { if (action.match(input, player)) { return true; } } return false; } public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("What is your name?"); String name = scanner.nextLine(); Player player = new Player(name); player.place = makeWorld(); System.out.println("Howdy there, Detective " + name+ "."); System.out.println(player.place.description); while (true) { System.out.print("> "); String line = scanner.nextLine(); if (line.startsWith("bye")) { break; } final Action look = getRegexpMatcher("look( (here|around))?") .then(p -> System.out.println(p.place.description)); Action[] actions = Stream.concat( Stream.concat( player.place.actions.stream(), player.items.stream().map(i -> i.action)), Arrays.stream(new Action[] { look })).toArray(Action[]::new); if (!match(actions, line, player)) { System.out.println("Wot?"); } } scanner.close(); } }