提交 abeacaee 编写于 作者: S SafeWinter

Update functional zork game

上级 54250cae
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>
bin
*.class
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>functional zork</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
<filteredResources>
<filter>
<id>1667407459743</id>
<name></name>
<type>30</type>
<matcher>
<id>org.eclipse.core.resources.regexFilterMatcher</id>
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>
# fpzork # 1024 程序员开源挑战赛项目 —— fpzork
用于练习 Java 函数式编程的一个命令行小游戏 ---
\ No newline at end of file
## 1. 项目概述
`fpzork` 是一个用于练习 `Java` 函数式编程的一个命令行小游戏,是 `functional programming: zork` 的简称。核心代码改编自 2015 年 `Packt` 出版设出版的《Learning Java Functional Programming》开源示例项目。
## 2. 运行方式
按普通 `Java` 项目导入您熟悉的 `IDE` 后(如 `Eclipse`),无需下载其它第三方依赖,即可直接运行项目。运行方式至少有以下三种方式:(以 `Eclipse` 为例)
1. 选中项目后,右键菜单 <kbd>Run As</kbd> :arrow_right: <kbd>Java Application   Alt+Shift+X,J</kbd>
2. 打开 `FunctionalZork.java`,运行 `main` 方法;
3. 导出项目为可执行 `jar` 文件(如 `zork.jar`)后,连同 `data.txt` 文件一同复制到某文件夹下(如桌面 `demo` 文件夹);通过命令行进入 `demo`,运行命令 `java -jar zork.jar`
无论以哪种方式运行成功后,都会看到像如下所示的命令行界面:(这里以 `Windows Terminal` 为例)
![Running Zork Game](img/running-game.png)
## 3 游戏操作命令
- 捡起物品:`pickup <Item1>[ <Item2>[ <Item3>]]`:拾起 `Item1``Item2``Item3``pickup` 也可写为 `Pickup`
- 放下物品:`drop <Item1>[ <Item2>[ <Item3>]]`:放下 `Item1``Item2``Item3``drop` 也可写为 `Drop`
- 前往某个方向:`go <Direction1>`:前往 `Direction1``go` 也可换作 `walk``Walk`
- 查看当前位置:`look`
- 查看可以前往的方向:`dir | direction`
- 查看拥有的物品:`inv | inventory`
- 结束游戏:`quit`
\ No newline at end of file
Location
House
You are standing outside a house sitting in the middle of a meadow. There is a path leading north to the woods and a path leading east to a lake.
Direction
North
Woods
Direction
East
Emerald Blue Lake
Item
Axe
A shape wooden handle axe
Item
Stone
A small round rock
Location
Woods
You are standing in the middle of a dense woods. There is a path leading south to a meadow and a path leading east to some small hills.
Direction
South
House
Direction
East
Small Hills
Location
Small Hills
You are standing on the top of a small hill. To the west you see a path leading to a vast forest. To the south is a path leading to an emerald blue lake.
Direction
West
Woods
Direction
South
Emerald Blue Lake
Item
Key
A small bronze key
Location
Emerald Blue Lake
You are standing on the shores of the lake you are impressed by it deep emerald blue color. There is a path leading north to some small hills and a path leading west to a meadow.
Direction
West
House
Direction
North
Small Hills
NPC
Orge
A fierce ugly orge lurks a few meters away. He looks like he is about to attack.
StartingLocation
House
\ No newline at end of file
package fpzork;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class Character {
private final List<String> items = new ArrayList<>();
private Location location;
public Character(Location currentLocation) {
this.location = currentLocation;
}
public boolean pickup(Command command) {
List<String> arguments = command.getArguments();
arguments.stream()
.filter(itemName -> {
if (this.location.getItems().contains(itemName)) {
return true;
} else {
System.out.println("Cannot pickup " + itemName);
return false;
}
})
.forEach(itemName -> {
items.add(itemName);
this.location.getItems().remove(itemName);
System.out.println("Picking up " + itemName);
});
return true;
}
public boolean drop(Command command) {
List<String> arguments = command.getArguments();
if (arguments.isEmpty()) {
System.out.println("Drop what?");
return false;
} else {
boolean droppedItem = false;
// Functional solution - does not modify droppedItem
// arguments.stream()
// .map(itemName -> {
// if (items.remove(itemName)) {
// this.location.addItem(itemName);
// return "Dropping " + itemName;
// } else {
// return "Cannot drop " + itemName;
// }
// })
// .forEach(System.out::println);
for (String itemName : arguments) {
droppedItem = items.remove(itemName);
if (droppedItem) {
this.location.addItem(itemName);
System.out.println("Dropping " + itemName);
droppedItem = true;
} else {
System.out.println("Cannot drop " + itemName);
}
}
return droppedItem;
}
}
public boolean walk(Command command) {
List<String> directions = command.getArguments();
if (directions.isEmpty()) {
System.out.println("Go where?");
return false;
} else {
directions.forEach((direction) -> {
Optional<String> locationName = GameElements.currentLocation.getLocation(direction);
System.out.print(locationName
.map(name -> {
Location newLocation = GameElements.locations.get(name);
this.location = newLocation;
GameElements.currentLocation = newLocation;
GameElements.displayView(GameElements.currentLocation);
return "";
})
.orElse("However, you can't go " + direction + "\n"));
});
return true;
}
}
public boolean inventory(Command command) {
//List<String> arguments = command.getArguments();
if (items.isEmpty()) {
System.out.println("You are holding nothing");
} else {
System.out.print("You are holding:");
this.items.forEach((item) -> {
System.out.print(" " + item);
});
System.out.println();
}
return true;
}
}
package fpzork;
import java.util.ArrayList;
import java.util.List;
public class Command {
private String command;
private final List<String> arguments = new ArrayList<>();
public void setCommand(String command) {
this.command = command;
}
public String getCommand() {
return command;
}
public void addArgument(String argument) {
this.arguments.add(argument);
}
public List<String> getArguments() {
return arguments;
}
public void clear() {
this.command = "";
this.arguments.clear();
}
}
package fpzork;
public class Direction {
private String direction;
private String location;
public Direction() {
this.direction = "";
this.location = "";
}
public Direction(String direction, String location) {
this.direction = direction;
this.location = location;
}
public String getDirection() {
return direction;
}
public Direction direction(String direction) {
this.direction = direction;
return this;
}
public String getLocation() {
return location;
}
public Direction location(String location) {
this.location = location;
return this;
}
}
package fpzork;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
public class FunctionalCommands {
private final List<Supplier<Boolean>> commands = new ArrayList<>();
public void addCommand(Supplier<Boolean> command) {
commands.add(command);
}
public void executeCommand() {
commands.forEach(Supplier::get);
commands.clear();
}
}
package fpzork;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
import java.util.function.Supplier;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import static fpzork.GameElements.commands;
public class FunctionalZork {
private final Scanner scanner;
private Character character = null;
private final FunctionalCommands fc;
private final Command command = new Command();
private Supplier<Boolean> dropCommand = () -> character.drop(command);
private Supplier<Boolean> pickupCommand = () -> character.pickup(command);
private Supplier<Boolean> walkCommand = () -> character.walk(command);
private Supplier<Boolean> inventoryCommand = () -> character.inventory(command);
private Supplier<Boolean> lookCommand = () -> {
GameElements.displayView(GameElements.currentLocation);
return true;
};
private Supplier<Boolean> directionsCommand = () -> {
GameElements.currentLocation.displayPaths();
return true;
};
private final Supplier<Boolean> quitCommand = () -> {
System.out.println("Thank you for playing!");
return true;
};
public void initializeCommands() {
commands.put("drop", dropCommand);
commands.put("Drop", dropCommand);
commands.put("pickup", pickupCommand);
commands.put("Pickup", pickupCommand);
commands.put("walk", walkCommand);
commands.put("go", walkCommand);
commands.put("inventory", inventoryCommand);
commands.put("inv", inventoryCommand);
commands.put("look", lookCommand);
commands.put("directions", directionsCommand);
commands.put("dir", directionsCommand);
commands.put("quit", quitCommand);
}
public static void main(String[] args) {
String command = "";
Stream<String> commandStream;
FunctionalZork game = new FunctionalZork();
while (!"Quit".equalsIgnoreCase(command)) {
System.out.print(">> ");
commandStream = game.getCommandStream();
game.parseCommandStream(commandStream);
command = game.executeCommand();
}
}
public FunctionalZork() {
scanner = new Scanner(System.in);
fc = new FunctionalCommands();
initializeGame();
character = new Character(GameElements.currentLocation);
}
public void initializeGame() {
System.out.println("Welcome to Functional Zork!\n");
File file = new File("data.txt");
try (FileInputStream fis = new FileInputStream(file);
BufferedReader br = new BufferedReader(new InputStreamReader(fis))) {
String line = br.readLine();
while ("Location".equalsIgnoreCase(line)) {
Location location = new Location()
.name(br.readLine())
.description(br.readLine());
line = br.readLine();
while ("Direction".equalsIgnoreCase(line)) {
// Add direction
location.addDirection(new Direction()
.direction(br.readLine())
.location(br.readLine())
);
line = br.readLine();
}
while ("Item".equalsIgnoreCase(line)) {
// Add items
Item item = new Item()
.name(br.readLine())
.description(br.readLine());
location.addItem(item.getName());
GameElements.items.put(item.getName(), item);
line = br.readLine();
}
while ("NPC".equalsIgnoreCase(line)) {
// Add NPC
// Imperative solution
NPC npc;
// npc = new NPC();
// npc.setName(br.readLine());
// npc.setDescription(br.readLine());
// Functional solution
npc = new NPC()
.name(br.readLine())
.description(br.readLine());
location.addNPC(npc.getName());
GameElements.npcs.put(npc.getName(), npc);
line = br.readLine();
}
GameElements.locations.put(location.getName(), location);
}
if ("StartingLocation".equalsIgnoreCase(line)) {
GameElements.currentLocation = GameElements.locations.get(br.readLine());
GameElements.displayView(GameElements.currentLocation);
} else {
System.out.println("Missing Starting Location");
}
initializeCommands();
// Test imperative partial solution
// executeCommandImperative("drop axe and rope");
} catch (IOException ex) {
ex.printStackTrace();
}
}
public String additionalProcessing(String token) {
if (token.equalsIgnoreCase("drop")) {
// Additional processing
}
return token;
}
public List<String> processCommand(String commandLine) {
// Imperative solution
// Stop words
List<String> toRemove = Arrays.asList("a", "an", "the", "and");
List<String> tokens = new ArrayList<>();
for (String token : commandLine.split("\\s+")) {
if (!toRemove.contains(token)) {
// tokens.add(token);
tokens.add(token.toLowerCase());
// First approach
String tmp = token.toLowerCase();
tmp = additionalProcessing(tmp);
tokens.add(tmp);
// Second approach
tokens.add(additionalProcessing(token.toLowerCase()));
}
}
return tokens;
}
public Stream<String> getCommandStream() {
String commandLine = scanner.nextLine();
// processCommand(commandLine);
// Stop words
List<String> toRemove = Arrays.asList("a", "an", "the", "and");
Stream<String> commandStream = Pattern
.compile("\\s+")
.splitAsStream(commandLine)
.map(s -> additionalProcessing(s))
//.map(s -> s.toLowerCase())
.filter(s -> !toRemove.contains(s));
return commandStream;
}
public void parseCommandStream(Stream<String> tokens) {
command.clear();
tokens
.map(token -> {
if (commands.containsKey(token)) {
// Make sure this is done only once
command.setCommand(token);
} else {
command.addArgument(token);
}
return token;
})
.allMatch(token -> !"quit".equalsIgnoreCase(token));
}
public void executeDropCommand(String command, String arguments[]) {
// Execute command
System.out.print("Command: " + command);
for (String arg : arguments) {
System.out.print(" " + arg);
}
System.out.println();
}
public void executeCommandImperative(String commandLine) {
// Imperative solution variation
String tokens[] = commandLine.split("\\s+");
String arguments[] = new String[tokens.length-1];
int index = 0;
String cmd = tokens[0];
if (cmd.equalsIgnoreCase("drop")) {
// Setup drop command
while (index+1 < tokens.length) {
arguments[index] = tokens[index+1];
index++;
}
executeDropCommand(cmd, arguments);
} else if (cmd.equalsIgnoreCase("pickup")) {
// Setup drop command
} else if (cmd.equalsIgnoreCase("go")) {
// Setup drop command
} else {
// Bad command
}
}
public String executeCommand() {
Supplier<Boolean> nextCommand = commands.get(command.getCommand());
if (nextCommand != null) {
fc.addCommand(nextCommand);
fc.executeCommand();
return command.getCommand();
} else {
System.out.println("Bad commmand");
return "";
}
}
}
package fpzork;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Supplier;
public class GameElements {
public static Map<String, Location> locations = new HashMap<>();
public static Map<String, Item> items = new HashMap<>();
public static Map<String, NPC> npcs = new HashMap<>();
public static final Map<String, Supplier<Boolean>> commands = new HashMap<>();
public static Location currentLocation;
public static void displayLocation(Location location) {
System.out.println("-------------------");
System.out.println("Name: " + location.getName());
System.out.println("Description: " + location.getDescription());
System.out.println("Items -------- ");
location.getItems().forEach(name -> System.out.println(GameElements.items.get(name)));
location.getNPCs().forEach(name -> System.out.println(GameElements.npcs.get(name)));
System.out.println();
}
public static void displayView(Location location) {
System.out.println(location.getDescription());
GameElements.currentLocation.displayItems();
GameElements.currentLocation.displayNPCs();
}
public static void displayDirections(Location location) {
GameElements.currentLocation.displayPaths();
}
}
package fpzork;
public class Item {
private String name;
private String description;
public String getName() {
return name;
}
public Item name(String name) {
this.name = name;
return this;
}
public String getDescription() {
return description;
}
public Item description(String description) {
this.description = description;
return this;
}
@Override
public String toString() {
return "Name: " + this.name + " Description: " + this.description;
}
}
package fpzork;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
public class Location {
private String name;
private String description;
private final List<String> items = new ArrayList<>();
private final List<String> npcs = new ArrayList<>();
private final Map<String, Direction> directions = new HashMap<>();
public String getName() {
return name;
}
public Location name(String name) {
this.name = name;
return this;
}
public String getDescription() {
return description;
}
public Location description(String description) {
this.description = description;
return this;
}
public List<String> getItems() {
return this.items;
}
public void addItem(String item) {
this.items.add(item);
}
public List<String> getNPCs() {
return npcs;
}
public void addNPC(String npc) {
this.npcs.add(npc);
}
public Optional<String> getLocation(String direction) {
if (this.directions.containsKey(direction)) {
return Optional.of(this.directions.get(direction).getLocation());
} else {
return Optional.empty();
}
}
public void addDirection(Direction direction) {
directions.put(direction.getDirection(), direction);
}
public void displayItems() {
if (items.isEmpty()) {
// Do nothing
} else {
System.out.print("On the ground you see:");
items.stream()
.forEach(item -> {
System.out.print(" " + item);
});
System.out.println();
}
}
public void displayNPCs() {
if (npcs.isEmpty()) {
// Do nothing
} else {
npcs.forEach(System.out::println);
}
}
public void displayPaths() {
directions.forEach((way, direction) -> {
System.out.print("If you go " + way);
System.out.println(" you can get to " + direction.getLocation());
});
}
}
package fpzork;
public class NPC {
private String name;
private String description;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public NPC name(String name) {
this.name = name;
return this;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public NPC description(String description) {
this.description = description;
return this;
}
@Override
public String toString() {
return "Name: " + name + " Description: " + description;
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册