Instructions
Requirements and Specifications
Screenshots
Source Code
ENCOUNTER
public class Encounter{
// Encounter Stats
private String statToCheck;
private int difficulty; // difficulty of the stat check
private Item reward;
private int risk; // how much stress to gain if you lose.
// Encounter Messages to print.
private String name = "Name";
private String description = "Doesn't look like much of anything";
private String winningDescription = " You win";
private String losingDescription = "You lose!";
private int id = -1; // no id until set.
// Encounter something at a place
// Constructor accepting the skillType, difficulty, reward Item and risk.
// Note the descriptions are not initialized and you should use setDescriptions
public Encounter(String stat, int diff, Item reward, int risk){
statToCheck = stat;
difficulty = diff;
// Alternate example of setting variables.
this.reward = reward;
this.risk = risk;
}
public void setDescriptions(String name, String basic, String win, String lose){
this.name = name;
description = basic;
winningDescription = win;
losingDescription = lose;
}
// Added A2
public int getId(){
return id;
}
// Added A2
public void setId(int newId){
id = newId;
}
public String getName(){
return name;
}
public String getDescription(){
return description;
}
public String getWinning(){
return winningDescription;
}
public String getLosing(){
return losingDescription;
}
public int getRisk(){
return risk;
}
// Note I am removing the Item but also setting the original variable to null
// to avoid duplicates.
public Item removeReward(){
Item temp = reward;
reward = null;
return temp;
}
public String getStatToCheck(){
return statToCheck;
}
public int getDifficulty(){
return difficulty;
}
// Added for testing
public String fullOutput(){
String s = id + "\n";
s += name + "\n";
s += difficulty + " " + risk + "\n";
s += reward.toString() + "\n";
s += description + "\n";
s += winningDescription + "\n";
s += losingDescription + "\n";
return s;
}
}
FILE LOADER
import java.io.File;
import java.util.Arrays;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.stream.Collectors;
/**
* Helper class for reading maps from files
*/
public class FileLoader {
/**
* Reads map from file with given name
* @param filename path to file for reading
* @return Map if file was read successfully, null --- otherwise
*/
public static Map loadMap(String filename) {
try(Scanner scanner = new Scanner(new File(filename))) {
String[] size = scanner.nextLine().split(" ");
int rows = Integer.parseInt(size[0]);
int cols = Integer.parseInt(size[1]);
scanner.nextLine();
char[][] mapTemplate = new char[rows][cols];
for (int i = 0; i
String line = scanner.nextLine().trim();
for (int j = 0; j
mapTemplate[i][j] = line.charAt(j);
}
}
return new Map(mapTemplate);
}
catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
return null;
}
}
/**
* Reads hero from file with given name
* @param heroFile path to file for reading
* @return Hero if file was read successfully, null --- otherwise
*/
public static Hero loadHero(String heroFile) {
try(Scanner scanner = new Scanner(new File(heroFile))) {
// reading hero parameters line by line, cutting away comments(smth after '//')
String name = scanner.nextLine().split("//")[0].trim();
String description = scanner.nextLine().split("//")[0].trim();
String stressParams = scanner.nextLine().split("//")[0].trim();
String[] stressParamsSplit = stressParams.split(" ");
int currentStress = Integer.parseInt(stressParamsSplit[0]);
int stressLimit = Integer.parseInt(stressParamsSplit[1]);
Hero hero = new Hero(name, description, currentStress, stressLimit);
String skillsParams = scanner.nextLine().split("//")[0].trim();
String[] skillsParamsSplit = skillsParams.split(" ");
int physical = Integer.parseInt(skillsParamsSplit[0]);
int mental = Integer.parseInt(skillsParamsSplit[1]);
int social = Integer.parseInt(skillsParamsSplit[2]);
hero.setSkills(physical, mental, social);
return hero;
}
catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
return null;
}
}
/**
* Reads encounters from file with given name
* @param filename path to file for reading
* @return Encounter array if file was read successfully, null --- otherwise
*/
public static Encounter[] loadEncounters(String filename) {
try(Scanner scanner = new Scanner(new File(filename))) {
// reading number of encounters in file
int encountersNum = Integer.parseInt(scanner.nextLine().split("//")[0].trim());
Encounter[] encounters = new Encounter[encountersNum];
// reading ecnountersNum of encounters file
for (int i = 0; i
// skipping empty line
scanner.nextLine();
// reading encounter parameters line by line, cutting away comments(smth after '//')
int encounterId = Integer.parseInt(scanner.nextLine().split("//")[0].trim());
assert encounterId == i;
String name = scanner.nextLine().split("//")[0].trim();
String diffRisk = scanner.nextLine().split("//")[0].trim();
String[] diffRiskSplit = diffRisk.split(" ");
int diff = Integer.parseInt(diffRiskSplit[0]);
int risk = Integer.parseInt(diffRiskSplit[1]);
String stat = scanner.nextLine().split("//")[0].trim();
Item item = new Item(scanner.nextLine().split("//")[0].trim());
encounters[i] = new Encounter(stat, diff, item, risk);
String description = scanner.nextLine().split("//")[0].trim();
String winningDescription = scanner.nextLine().split("//")[0].trim();
String losingDescription = scanner.nextLine().split("//")[0].trim();
encounters[i].setDescriptions(name, description, winningDescription, losingDescription);
}
return encounters;
}
catch (Exception e) {
System.out.println(e.getMessage());
e.printStackTrace();
return null;
}
}
public static void main(String[] args) {
Map map = FileLoader.loadMap("mapFile.txt");
if (map != null) {
System.out.println(map);
}
Hero hero = FileLoader.loadHero("heroFile.txt");
System.out.println(hero);
Encounter[] encounters = FileLoader.loadEncounters("encountersFile.txt");
assert encounters != null;
System.out.println(Arrays.stream(encounters).map(Encounter::getName).collect(Collectors.joining(", ")));
}
}
GAME CONTROLLER
import java.awt.event.KeyEvent;
/*
This class will be our main gameplay loop (for now).
It will later include a turn counter (turn based) and listen for input from the player.
Think of simulation type games such as the Sims. This is a very simple version.
We will increase the complexity later.
*/
public class GameController {
// Its a party, only simulated. Generate some guests, assign them to mingle, then print them out in the end (including who they talked to).
public static void main(String[] args) {
// reading map file
Map map = FileLoader.loadMap("mapFile.txt");
if (map == null) {
// error occurred reading map file. Return
return;
}
map.initHeroPosition();
// reading hero file
Hero theHero = FileLoader.loadHero("heroFile.txt");
if (theHero == null) {
// error occurred reading hero file. Return
return;
}
System.out.println("New Hero Created!");
System.out.println(theHero.fullToString());
// reading encounters file
Encounter[] events = FileLoader.loadEncounters("encountersFile.txt");
if (events == null) {
// error occurred reading encounters file. Return
return;
}
// setting counter of met encounters to 0
int counter = 0;
// showing initial map
System.out.println(map);
// in cycle, while hero's status is OK and unvisited encounters remaining
while (counter < events.length && theHero.checkStatus()) {
// reading user input
int i = getInput();
// checking if user pressed ArrowKeys
if (i >= 0 && i < 4) {
// processing user input
char c = map.processInput(i);
// if digit was returned, an encounter was met
if (Character.isDigit(c)) {
// getting encounter id from char
int eNum = Character.getNumericValue(c);
// facing encounter
nextEncounter(theHero, events[eNum]);
counter++;
}
// showing updated map
System.out.println(map);
}
StdDraw.show(200);
}
endGame(theHero);
}
// True means the player won. Apply the damage or reward within this method as well.
private static void nextEncounter(Hero hero, Encounter event) {
System.out.println("--------------------------------------");
System.out.println(event.getDescription());
boolean heroPassed = hero.skillCheck(event.getStatToCheck(), event.getDifficulty());
if (heroPassed) {
System.out.println(hero.getName() + event.getWinning());
// Hero Succeeds
Item temp = event.removeReward();
System.out.println(hero.getName() + " found a " + temp.toString() + " in the " + event.getName());
hero.addItem(temp);
} else {
System.out.println(hero.getName() + event.getLosing());
// Hero fails.
hero.addStress(event.getRisk());
System.out.println("Our Hero " + hero.getName() + " gained " + event.getRisk() + " stress from their loss at " + event.getName());
}
//return heroPassed;
}
private static void endGame(Hero finalHero) {
System.out.println("--------------------------------------");
if (finalHero.checkStatus()) {
System.out.println(finalHero.getName() + " was successful!");
} else {
System.out.println(finalHero.getName() + " passed out from stress!");
}
System.out.println(" === Final Output === ");
System.out.println(finalHero.fullToString());
System.out.println("Thank you for playing. The End");
}
private static int getInput() {
if (StdDraw.isKeyPressed(KeyEvent.VK_UP)) {
return 0;
}
if (StdDraw.isKeyPressed(KeyEvent.VK_RIGHT)) {
return 1;
}
if (StdDraw.isKeyPressed(KeyEvent.VK_DOWN)) {
return 2;
}
if (StdDraw.isKeyPressed(KeyEvent.VK_LEFT)) {
return 3;
}
return -1;
}
}
HERO
// A Hero represents (as you might expect) a single Hero. There should
// only ever be one instance of each Hero at a time.
public class Hero{
// Phase 1
private String name; // Name of the Hero
private String description; // Description of the Hero.
private int currentStress;
private int stressLimit; // reach this and you are done. Pass out.
private boolean isAwake = true; // if hp = 0, pass out.
// Stats - min 1, max 5
// make up your own skills if you want
private int physicalSkill; // strength, agility, constitution
private int mentalSkill; // intelligence, wisdom, mental sharpness
private int socialSkill; // charisma, empathy and other group skills
// private String[] traits; // Like tags, can have other abilities.
private ItemList inventory; // All Items collected so far
// ******* Initialization *******
public Hero(){
name = "Default";
description = "None Given";
currentStress = 0;
stressLimit = 10;
inventory = new ItemList();
}
// basic constructor.
public Hero(String newName, String newDescription, int currentStress, int stressLimit){
name = newName;
description = newDescription;
// stress
this.currentStress = currentStress;
this.stressLimit = stressLimit;
// set up items.
inventory = new ItemList();
}
// Option B1:
// Could also be done in the constructor if you wanted.
public void setSkills(int newPhysical, int newMental, int newSocial){
physicalSkill = newPhysical; // strength, agility, constitution
mentalSkill = newMental; // intelligence, wisdom, mental sharpness
socialSkill = newSocial;
}
// Return only the name for now.
public String toString(){
return name;
}
// p1
// Return the name
public String getName(){
return name;
}
// "Accessor" method for description
public String getDescription(){
return description;
}
// include description and skills as well.
public String profileToString(){
return name + "\n" + description
+ "\n - Stress: " + currentStress + "/" + stressLimit;
}
// Return the skills only.
public String skillsToString(){
return " - physical: " + physicalSkill
+ "\n - mental:" + mentalSkill
+ "\n - social:" + socialSkill;
}
// P3
// Items
public void addItem(Item newItem){
inventory.addItem(newItem);
}
public String allItems(){
return inventory.toString();
}
// Name, Description, all Skills and All Items.
public String fullToString(){
return profileToString() + "\n" + skillsToString() + "\n" + allItems();
}
// Alternate way of assigning variables, using "this" keyword
// "Mutator" method for description
// For now I am overwriting the old description. This could be improved
// by allowing for appending.
public void setDescription(String description){
this.description = description;
}
// Phase 5 - Still needs doing.
// Checks a skill against the stat of the Hero.
// If the value is less then the given skill plus a random value between 1-4. return true
// Use strings for now to check type
// "mental", "physical", "social"
// True means you passed.
public boolean skillCheck(String skillType, int value){
boolean outcome = false;
int baseRoll = RandomGenerator.randomRoll(1,4);
System.out.println(" - DEBUG: RANDOM ROLL: "+ baseRoll);
if(skillType.equals("mental")){
if(value <= mentalSkill + baseRoll){
outcome = true;
}
}else if(skillType.equals("physical")){
if(value <= physicalSkill + baseRoll){
System.out.println(" - DEBUG: physicalSkill + baseRoll " + physicalSkill + ' ' + baseRoll);
outcome = true;
}
}else if(skillType.equals("social")){
if(value <= socialSkill + baseRoll){
outcome = true;
}
}else{
// a character with no skill should check with level 0.
// helps with bugs, should not be called.
System.out.println("Warning: Unknown skill found: " + skillType);
}
return outcome;
}
// add negative to remove it
// this is kind of problematic.
public void addStress(int amount){
currentStress = currentStress + amount;
}
// Status of true means things are ok to go on
// status of false means they have passed out.
public boolean checkStatus(){
boolean returnVal = false;
if(isAwake){
if(currentStress < stressLimit){
// Things are OK
returnVal = true;
}else{
// trigger the pass out.
isAwake = false;
}
}else{
// no way to wake up yet
}
return returnVal;
}
}
ITEM
// Dylan Fries 2019
// The Item Class represents a single Item with a randomly generated name
public class Item{
private String itemName;
public Item(){
itemName = RandomGenerator.getRandomItemName();
}
// An overloaded constructor that accepts a custom item name.
// This may be useful in debugging to ensure the ItemList is working as expected.
public Item(String newName) {
itemName = newName;
}
// returns the itemName. Don’t add additional formatting here.
public String toString() {
return itemName;
}
}
MAP
/**
* Class, representing flat rectangular map
*/
public class Map {
/**
* Number of rows (or height) in map
*/
private final int rows;
/**
* Number of cols (or width) in map
*/
private final int cols;
private int heroRow;
private int heroCol;
/**
* 2-dimensional array, storing content of each cell on the map
*/
private final char[][] map;
/**
* Creates empty map of the given size
* @param rows height of the map
* @param cols width of the map
*/
public Map(int rows, int cols) {
// Assigning size variables
this.rows = rows;
this.cols = cols;
// Iterating over all cells, assigning empty content char ('.')
this.map = new char[rows][cols];
for (int i = 0; i
for (int j = 0; j
map[i][j] = '.';
}
}
}
/**
* Creates map from given content
* @param mapTemplate content of each cell on the map
*/
public Map(char[][] mapTemplate) {
// Assigning map size variables
this.rows = mapTemplate.length;
this.cols = mapTemplate[0].length;
// Iterating over rows of given content, assigning row by row
this.map = new char[rows][cols];
for (int i = 0; i
char[] row = mapTemplate[i];
// Ensuring, that all rows have the same length
assert cols == row.length;
// Copying row
System.arraycopy(mapTemplate[i], 0, map[i], 0, cols);
}
}
/**
* Adds walls to the bounds of map
*/
public void addWalls() {
// Adding vertical rows
for (int i = 0; i
map[i][0] = 'X';
map[i][cols-1] = 'X';
}
// ADding horizontal rows
for (int i = 0; i
map[0][i] = 'X';
map[rows-1][i] = 'X';
}
}
/**
* Setting content of specific tile (cell)
* @param row of tile to set
* @param col of tile to set
* @param value to set
* @return true, if assignment was successful, false --- otherwise
*/
public boolean setTile(int row, int col, char value) {
// if map is not assigned yet, return error
if (map == null) {
System.out.println("Error: map is null");
return false;
}
// if tile position is out of bounds, return error
if (row < 0 || row >= rows || col < 0 || col >= cols) {
System.out.println("Error: map position is out of bounds");
return false;
}
// setting tile
map[row][col] = value;
return true;
}
/**
* Assigning hero position variables according to current map content
*/
public void initHeroPosition() {
for (int i = 0; i
for (int j = 0; j
// if symbol 'H' found, assigning variables
if (map[i][j] == 'H') {
heroRow = i;
heroCol = j;
return;
}
}
}
System.out.println("Warning: hero is not found on the map");
}
/**
* Processing direction, input by user
* @param direction which was input by user
* @return character of new tile, visited by user
*/
public char processInput(int direction) {
// creating end position variables equal to current position
int endRow = heroRow;
int endCol = heroCol;
// changing end position variable according to direction chosen
switch (direction) {
case 0:
endRow--;
break;
case 1:
endCol++;
break;
case 2:
endRow++;
break;
case 3:
endCol--;
break;
}
// if move is valid, making it
if (isValidMove(endRow, endCol)) {
return applyMove(heroRow, heroCol, endRow, endCol);
}
// otherwise, returning wall symbol
return 'X';
}
/**
* Checking if move is valid
* @param row to move into
* @param col to move int
* @return true, if new tile is in bounds and is not wall, false --- otherwise
*/
private boolean isValidMove(int row, int col) {
return (row >= 0 && row < rows && col >= 0 && col < cols) && map[row][col] != 'X';
}
/**
* Aplpies move to current map and hero location, updates hero position
* @param startRow row from which hero moves
* @param startCol col from which hero moves
* @param endRow row to which hero moves
* @param endCol col to which hero moves
* @return character of the new visited tile
*/
private char applyMove(int startRow, int startCol, int endRow, int endCol) {
map[startRow][startCol] = '.';
char result = map[endRow][endCol];
heroRow = endRow;
heroCol = endCol;
map[heroRow][heroCol] = 'H';
return result;
}
/**
* Returns the string representation of the map
* @return String representation where each tile is a char
*/
@Override
public String toString() {
// Constructing result string using StringBuilder
StringBuilder builder = new StringBuilder();
for (int i = 0; i
for (int j = 0; j
builder.append(map[i][j]);
}
// Adding line separator between rows
builder.append(System.lineSeparator());
}
return builder.toString();
}
public static void main(String[] args) {
Map m = new Map(3,4);
m.addWalls();
System.out.println(m.toString());
}
}
RANDOM GENERATOR
// Random Generator
// A Utility class for generating random values for things. Includes some random number selection
// as well as Name, Location, Item and Creature sets.
// - by Dylan Fries -
public class RandomGenerator{
// ======== Source Words ========
// [ ] Update with better adjectives and nouns for places to party. You should have a minimum of 5 of each but more is better
// --- Locations ---
// a random list of words to generate a random location
private static String[] locationAdjectives = {"Cold","Damp","Soggy","Hot","Dangerous","Dark","Bright","Cavernous", "Smelly","Charred","Glowing","Dirty","Spotless","Marble","Glass"};
private static String[] locationNouns = {"Cave","Hallway","Diner","Room","Throne Room","Quarters","Kitchen","Armory","Garage","Stables","Pit","Bedroom","Workshop", "Tunnel","Plateau"};
// --- Names ---
private static String[] randomNames = {"Hero","Hero2","Hero3"}; // [ ] Add names for Hero here.
// --- Item generation ---
// Not used in A1 (yet)
// Generate a random item name
private static String[] itemAdjectives = {"Magic","Antique","Smelly","Rusted","Broken","Fragile","Glowing","Strange","Flaming"};
private static String[] itemNouns = {"Hammer","Shield","Helmet","Shoes","Sword","Ax","Abicus","Jar","Boots","Gloves","Ring"};
// Not used in A1 (yet)
// this list is sorted by difficulty
private static String[] creatureDifficulty = {"Insignificant","Trivial","Tiny","Small","Weak","Rumpled","Sulking","Zombie","Mutant","Angry","Venomous","Fierce","Giant","Tremendous","Elemental","Monsterous","Omnipotent","Exceptional"};
private static String[] creatureNames = {"Mouse","Lizard","Rabbit","Skunk","Fish","Monkey","Crab","Blob","Goblin","Dinosaur","Orc","Dragon","Bandit","Werewolf"};
// ======== Random Generators ========
// This method returns names of monsters.
// The type of monster is determined by the difficulty of the location
// we can use the difficulty as an index to get a monster from the list
// Difficulty is locked from 0 to 6. total 18 difficulty names
// This is a little loose, there are probably better ways to map this but I am dealing with
// uncertainty by contraining both the input and selected (calculated) indexes manually.
public static String getCreatureNameByDifficulty(int difficulty){
String returnMonster = "Default Monster";
// Lock in bounds. This should never be < 0 but might be higher then 6
if( difficulty < 0)
difficulty = 0;
if( difficulty > 6)
difficulty = 6;
// Add some random noise. The difficulty should be 'close' to where it actually will be but might be higher
int tilt = randomRoll(-1,4);
int adjustedDifficulty = difficulty * 3 + tilt;
// boundary check again because I am adding some random values to the index
if(adjustedDifficulty < 0)
adjustedDifficulty = 0; // zero difficulty
if( adjustedDifficulty >= creatureDifficulty.length )
adjustedDifficulty = creatureDifficulty.length -1; // max difficulty
// Select a name by combining difficulty and type
returnMonster = creatureDifficulty[adjustedDifficulty];
int typeIndex = randomRoll(0, creatureNames.length);
returnMonster += " " + creatureNames[typeIndex];
// Return the new generated monster name
return returnMonster;
}
// Get a random name of an Item
public static String getRandomItemName(){
int indexAdjective = randomRoll(0,itemAdjectives.length);
int indexNoun = randomRoll(0,itemNouns.length);
String returnName = itemAdjectives[indexAdjective] + " " + itemNouns[indexNoun];
return returnName;
}
// Generate a random persons name from the list supplied above
public static String getRandomName(){
int index = randomRoll(0,randomNames.length);
String name = randomNames[index];
return name;
}
// Just the description
// [ ] Upgrade this later.
// [ ] Longer sentence. Several words
// [ ] More complex grammer
// [ ] Generate appropriate Items from file.
public static String getRandomPlaceDescription(){
int index = randomRoll(0, locationAdjectives.length);
return locationAdjectives[index];
}
// Generates a random two word name for the Location and returns it.
// Sample names could include: "Cold Cave", "Damp Hallway", "Soggy Room" etc.
public static String getRandomLocationName(){
// randomly roll to get the adjective.
int indexAdjective = randomRoll(0, locationAdjectives.length);
// randomly roll to get the noun.
int indexNoun = randomRoll(0, locationNouns.length);
String returnName = locationAdjectives[indexAdjective] + " " + locationNouns[indexNoun];
return returnName;
}
// ======== Utility Functions ========
// Roll a random int between min (inclusive) and max (exclusive)
// - Pay attention to the edges when doing this. Off by one errors are common
// [ ] You should learn how this works, it is a very useful method
public static int randomRoll(int min, int max){
int randomNum = 0;
// Get the random number between [0..1)]
double random = Math.random();
// convert to an int range from [0..length-1]
int range = max - min;
randomNum = min + (int)(random * range);
return randomNum;
}
}