Assignment 5: Building a game; Visitors
Goals: To build a non-trivial Big-bang game from scratch, and practice with visitors
Instructions
the names of classes,
the names and types of the fields within classes,
the names, types and order of the arguments to the constructor,
the names, types and order of arguments to methods, or
filenames,
Make sure you follow the style guidelines that Handins enforces. For now the most important ones are: using spaces instead of tabs, indenting by 2 characters, following the naming conventions (data type names start with a capital letter, names of fields and methods start with a lower case letter), and having spaces before curly braces.
You will submit this assignment by the deadline using the Handins submission system. You may submit as many times as you wish. Be aware of the fact that close to the deadline the Handins system may slow down to handle many submissions - so try to finish early.
There will be a separate submission for each problem - it makes it easier to grade each problem, and to provide you with the feedback for each problem you work on.
The submissions will be organized as follows:
Homework 5 Problem 1: The IArith.java file
Homework 5 Problem 2: Your Sokoban game
Homework 5 Problem 2 review: Your peer review of other students’ designs
Problem 1, arithmetic visitors: Friday, Feb 14th, at 9:00pm
Problem 2, Sokoban implementation: Friday, Feb 14th, at 9:00pm
Problem 2 peer review: Saturday, Feb 15th, at 9:00pm
Problem 1: Visitors
+-------------------+ | IArith | +-------------------+ +-------------------+ /_\ /_\ | |------------------------------------------------------ | | | +-------------+ +------------------------------+ +-----------------------------------------+ | Const | | UnaryFormula | | BinaryFormula | +-------------+ +------------------------------+ +-----------------------------------------+ | double num | | Function<Double,Double> func | | BiFunction<Double, Double, Double> func | +-------------+ | String name | | String name | | IArith child | | IArith left | +------------------------------+ | IArith right | +-----------------------------------------+
Specifically, the above represents an arithmetic expression. The Function and BiFunction in UnaryFormula and BinaryFormula respectively denote the arithmetic operation to be applied to their respective operands (child and left,right). In class, we defined these interfaces as IFunc and IFunc2, but they’re actually predefined for you in Java. To use them, you will need to write import java.util.function.*; at the top of your file among the other import statements. As in class, these interfaces require that you implement a method named apply, whose parameters are (in this problem) all Doubles.
You must support 4 binary formulas (named "plus", "minus", "mul" and "div" representing addition, subtraction, multiplication and division respectively), and 2 unary formulas (named "neg" and "sqr" representing negation and squaring respectively).
Design an interface IArithVisitor<R> representing a visitor that visits an IArith and produces a result of type R. The visitor must be usable as a Function object on IArith producing a result of type R. For example, given an IArith object named iObj, and a SomeVisitor class, we should be able to write new SomeVisitor().apply(iObj).
Design an accept(IArithVisitor<R>) method for the IArith interface and implement it on Const, UnaryFormula and BinaryFormula.
Design an EvalVisitor that visits an IArith and evaluates the tree to a Double answer.
Design a PrintVisitor that visits an IArith and produces a String showing the fully-parenthesized expression in Racket-like prefix notation (i.e. "(div (plus 1.0 2.0) (neg 1.5))"), using the name for Formulas and using Double.toString(num) on Consts.
Design a DoublerVisitor that visits an IArith and produces another IArith, where every Const in the tree has been doubled.
Design an AllSmallVisitor that visits an IArith and produces a Boolean that is true if every constant in the tree is less than 10.
Tricky! Design a NoDivBy0 visitor that visits an IArith and produces a Boolean that is true if anywhere there is a Formula named "div", the right argument does not evaluate to roughly zero. Since all values here are double, define “roughly zero” as “absolute value less than 0.0001”.
When evaluating the result, the computer on which this program will run has no support for negative numbers. Design a NoNegativeResults visitor that visits an IArith and produces a Boolean that is true, if a negative number is never encountered at any point during its evaluation.
Problem 2: Sokoban, Part 1
Sokoban
We’re going to build Sokoban, a logic/puzzle game created in 1981, over the next few weeks. There are many variations on this game in existence, so we’ll be adding features as we go. Adding features may mean that you have to redesign earlier parts of your code, so the cleaner your initial design is, the easier it will be to modify later. Spending time early on careful, clean design will pay off!
A Sokoban level is played on a rectangular grid of square cells. For now we’ll only consider a few kinds of cells; we’ll add more later. Several things could be in a cell: nothing at all, the player, a brick wall, a crate, a trophy, or a target. (We’ll add more possible things in later assignments.) Here is an example board:
The goal of the game is to get all the colored trophies onto their similarly colored targets: as in the example above, your game should support four colors; our implementation uses red, green, blue and yellow. Some things in the game can move: crates, trophies, and the player. Others are fixed: the targets, and brick walls. You can play the original, classic version of the game at https://www.mathsisfun.com/games/sokoban.html, but be aware that this version only has movable crates, which behave like the trophies in our version of the game (i.e. the crates need to be placed on the targets).
Exercise
Design data definitions sufficient to describe the board state. The board above can indeed be thought of as rectangular: there are just a lot of blank cells and the brick walls just happen to enclose a smaller region of the board. You will need several helper data definitions. Note that different boards could easily be of different sizes, so your definitions should not limit themselves to a specific size of board. Be sure to follow the design recipe for all of them, and create sufficient examples. Your data definitions should accommodate new features in the future, so be careful not to make your designs too inflexible.
It will be convenient to have an easy way to write down board levels as text. Let’s distinguish things-on-the-ground from things-in-a-cell:
"Y", "G", "B" and "R" represent yellow, green, blue and red targets, on the ground
"_" represents a blank ground cell
"y", "g", "b" and "r" represent yellow, green, blue and red trophies
"W" represents a wall
"B" represents a box
">", "<", "^" and "v" represent the player facing right, left, up or down
Linebreaks "\n" can separate rows within the level
String exampleLevelGround = "________\n" + "___R____\n" + "________\n" + "_B____Y_\n" + "________\n" + "___G____\n" + "________"; String exampleLevelContents = "__WWW___\n" + "__W_WW__\n" + "WWWr_WWW\n" + "W_b>yB_W\n" + "WW_gWWWW\n" + "_WW_W___\n" + "__WWW___";
Exercise
You should design a class to represent a Sokoban level. One of the constructors for your class should take in level-description strings like these, and use them to configure the level —it will likely be easier than trying to write out the IList of contents by hand.
Exercise
Design a method on your class to render it as an image. You do not need to use precisely the same images as we have here, but if you’d like to, you may copy these images and use them:
Save them to your project’s directory, and use the FromFileImage class to create WorldImages from them.
Exercise
Design a method levelWon that detects if the player has won the level: it should check that every target has a trophy on top of it, whose color matches the target’s color. Note: you can design Sokoban levels where there are more trophies than targets, so you do not need to check if every trophy is on a target. (The converse, where there are more targets than trophies, is simply an unwinnable board.)
Exercise
Design a class that subclasses javalib.funworld.World, that contains a starting Sokoban level and that lets the player move in any direction. For now, ignore the full rules of the game: a player can move to any adjacent empty cell.
Hint: Describing a rectangular grid is best done using a list of lists. Creating large lists of lists may be quite tedious. Abstract out the convenience method above that takes in the string of ground-content and the string of cell-content and produces the requisite list of lists; test that method with several small but plausible examples; and then use that helper to build larger test cases.
Hint: it likely makes sense to have two separate classes, one that subclasses World and handles keypresses and such, and one that represents your game logic. Combining these into a single class risks making that class bloated and hard to read.
Submission notes
Don’t make the game overly elaborate; we are more interested in your program’s design than your graphic design! Include two additional files in your submission:
UserGuide.txt should be a short file describing how to play the game (so far! – you’ll update this in future parts as more of the game gets implemented).
Design.txt should describe each of the interfaces and classes in your design, and what they all do. If you think it’s helpful, include an ASCII-art class diagram to show the relationships between the classes.
Be sure to include all your source files and all your image files in your submission, or else we won’t be able to run your game. You should submit the files as a single .zip file. Zip up your source directory and your images – don’t include the out/ or target/ directory where Eclipse puts your compiled code.