Skip to content
Snippets Groups Projects
Commit 96c20691 authored by Christopher Bohn's avatar Christopher Bohn :thinking:
Browse files

Added starter code

parent 20877745
Branches
No related tags found
No related merge requests found
Showing
with 969 additions and 59 deletions
# PROJECT NAME # Yatzy
The [Wikipedia page for Yatzy](https://en.wikipedia.org/wiki/Yatzy) contains
the description of the game. You will implement a one-player version.
- There are five dice. In each turn, the player may roll some or all of the
dice up to three times.
- After the first or second roll, the player *may* score the dice against a
scoring category and end the turn. After the third roll, the player *must*
score the dice against a scoring category and end the turn.
- The scoring categories are generally grouped into the "Upper Section" and
the "Lower Section"; however, due to the display's aspect ratio, we will
refer to them as the "Left Section" and the "Right Section."
- The Left Section has these scoring categories:
- Ones: The sum of all dice showing 1 pip
- Twos: The sum of all dice showing 2 pips
- Threes: The sum of all dice showing 3 pips
- Fours: The sum of all dice showing 4 pips
- Fives: The sum of all dice showing 5 pips
- Sixes: The sum of all dice showing 6 pips
- Bonus (not player-selectable): if the Left Section's player-
selectable categories sum to 63 (an average of three dice in each
category), then the player receives a 50-point bonus
- The Right Section has these scoring categories:
- One Pair (2 dice showing the same number of pips), scored as the
sum of those 2 dice
- Two Pair (2 different pairs of dice), scored as the sum of the dice
in those pairs
- The face-value of the two different pairs *must* be different;
for example, 1-2-2-3-3 may be scored as Two Pair, but 1-2-2-2-2
may not be scored as Two Pair
- Three of a Kind (3 dice showing the same number of pips), scored as
the sum of those 3 dice
- Four of a Kind (4 dice showing the same number of pips), scored as
the sum of those 4 dice
- Small Straight (the combination of 1-2-3-4-5), scored as the sum of
those 5 dice
- Note that this is *not* the same definition of a "small
straight" in Yahtzee®
- Large Straight (the combination of 2-3-4-5-6), scored as the sum of
those 5 dice
- Note that this is *not* the same definition of a "large
straight" in Yahtzee®
- Full House (three of a kind and a pair), scored as the sum of those
5 dice
- The face-value of the pair *must* be different than the face-
value of the three of a kind; for example, 2-2-2-3-3 may be
scored as a Full House, but 2-2-2-2-2 may not be scored as Full
House
- Chance (any combination of dice), scored as the sum of those 5 dice
- Yatzy (5 dice showing the same number of pips), scored as 50 points
- Dice that do not conform to a scoring category's requirements may be
assigned to that category, but they will receive a score of 0.
\ No newline at end of file
...@@ -6,7 +6,9 @@ Due: June 12, 2020 at 11:00am ...@@ -6,7 +6,9 @@ Due: June 12, 2020 at 11:00am
In this assignment you will prepare a simple class diagram. In this assignment you will prepare a simple class diagram.
You will: ## Objectives
Students will:
- Prepare a simple class diagram - Prepare a simple class diagram
- Practice domain modeling under guidance - Practice domain modeling under guidance
......
Title,Description
Write AbstractSubject,Implement the methods in AbstractSubject to provide the behavior required of the subject in the Observer Pattern
Use the Observer Pattern,"Remove the line in which DiceController updates the view with each die's new value, and change Die.roll() and AbstractDieView.update() to work with the Observer Pattern"
Set up the Template Method Pattern,"Create a an abstract method in AbstractDieBasedCategory, and implement getHypotheticalScore to use it"
Implement NumberCategory,Implement the methods in NumberCategory for the Left-Side scoring categories
Implement AbstractMatchingCategory,Implement the method in the intermediate class for the categories that rely on dice matching other dice
Implement OfAKindCategory,"Implement the methods in OfAKindCategory for the Right-Side categories that expect only a single group of matching dice (One Pair, Three of a Kind, etc)"
Implement OfTwoKindsCategory,"Implement the methods in OfTwoKindsCategory for the Right-Side categories that expect two groups of matching dice (Two Pair, Full Hosue)"
Implement SequenceCategory,Implement the methods in SeqeuenceCategory for the Right-Side categories that expect a contiguous sequence of dice (Small & Large Striaghts)
Implement ChanceCategory,Implement the methods in ChanceCategory for the Right-Side category that accepts any dice
Create remaining number-based categories,Modify ScoreController.addLeftCategories to create the remaining Left-Side categories
Implement the execute method for die-based scoring commands,Implement AbstratDieBasedScoringCommand.execute
Implement command for one pair,Should be a subclass of AbstractDieBasedScoringCommand. Might be the same class as for other categories. Modfiy ScoreController.addRightCategories to create this category.
Implement command for two pair,Should be a subclass of AbstractDieBasedScoringCommand. Might be the same class as for other categories. Modfiy ScoreController.addRightCategories to create this category.
Implement command for three of a kind,Should be a subclass of AbstractDieBasedScoringCommand. Might be the same class as for other categories. Modfiy ScoreController.addRightCategories to create this category.
Implement command for four of a kind,Should be a subclass of AbstractDieBasedScoringCommand. Might be the same class as for other categories. Modfiy ScoreController.addRightCategories to create this category.
Implement command for small straight,Should be a subclass of AbstractDieBasedScoringCommand. Might be the same class as for other categories. Modfiy ScoreController.addRightCategories to create this category.
Implement command for large straight,Should be a subclass of AbstractDieBasedScoringCommand. Might be the same class as for other categories. Modfiy ScoreController.addRightCategories to create this category.
Implement command for yatzy,Should be a subclass of AbstractDieBasedScoringCommand. Might be the same class as for other categories. Modfiy ScoreController.addRightCategories to create this category.
\ No newline at end of file
...@@ -6,7 +6,9 @@ Due: June 16, 2020 at 11:00am ...@@ -6,7 +6,9 @@ Due: June 16, 2020 at 11:00am
In this assignment you will prepare a simple sequence diagram. In this assignment you will prepare a simple sequence diagram.
You will: ## Objectives
Students will:
- Prepare a sequence diagram - Prepare a sequence diagram
- Practice solution modeling under guidance - Practice solution modeling under guidance
......
# Yatzy Game
- Assignment Due: June 29, 2020 at 11:00am (CDT, UTC-5)
- Peer Assessment Due: June 29, 2020 at 11:59pm
In this assignment you will implement a simple dice game.
## Objectives
Students will:
- Gain additional experience developing software with a partner
- Gain experience using a version control system with multiple developers
- Gain experience using an issue tracker to coordinate efforts
- Demonstrate good software engineering practices
- Learn three design patterns
- Command Pattern
- Observer Pattern
- Template Method Pattern
- Learn the Model-View-Controller architectural style
## Instructions
This assignment is to be completed in assigned pairs; **no collaboration
other than with your assigned partner is permitted**. One of the purposes of
pair-assignments is to practice teamwork. After completing the assignment you
will need to complete a peer assessment. Your contribution grade will be based
on the peer assessments and on the git history.
*Commit material that you worked on individually under your own name* using the
defaults that you set. *When (and only when) you commit material that was
developed using pair programming, override the default commit author to reflect
both authors* so that we can properly credit both authors for their contribution
grades. When you override the default commit author list both students' names,
and for the email address use a fake email address that is unique to the pair
of students by concatenating your Canvas login IDs (the angle brackets around
the email address are required):
```
git commit --author="Herbie Husker and Lil Red <hhusker20lred19@dev.null>"
```
You can use this same technique for the rare circumstance in which your partner
is briefly unable to commit code themselves:
```
git commit --author="Herbie Husker <herbie@huskers.unl.edu>"
```
## Setup
1. You and your partner will work on a shared repository, which has been
prepared for you.
1. Navigate to your shared directory
(<https://git.unl.edu/csce_361/summer2020/12pairNN/>, where *NN* is your
team number).
1. Verify that the repository is private, and that you and your partner
both have Maintainer access.
1. Both students should:
1. Clone the project: `git clone <URL>` (here the angle brackets should
not be included).
- **Do *NOT* place your socket_chat repository inside your
csce361-homework repository!**
1. Import the project into your IDE. The project is set up as a Maven
project, so you can follow your IDE's instructions to import a Maven
project.
## Issue Tracker
We have pre-populated your repository's Issue Tracker with issues for the
various parts of the assignment that need to be completed. **We do *not*
guarantee that the pre-populated issues are complete; this document is the
authoritative source of requirements.**
- A good way to coordinate who is working on which parts of the code is to
use the [web interface](https://docs.gitlab.com/ee/user/project/issues/index.html#issue-page)
to "assign" an issue to a team member.
- You may [add more issues](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#create-a-new-issue)
to the Issue Tracker if you wish. This is common when you discover more
tasks that need to be accomplished or when you want to divide an existing
issue into finer-grained tasks (the original issue would still exist, but
the finer-grained issues may make it easier to divide the work).
- When you have completed a task, [close the corresponding issue](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues).
You can do this through the issue tracker, or you can do it through a
commit message.
- To close an issue through a commit message, include a keyword such as
`closes #12` in a commit message. You cannot create a commit message
only to close an issue; this must be a commit message for adding/
changing/removing files or for a merge. See <https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#default-closing-pattern>
for keywords that will close an issue.
## Assignment
Procrastination Pastimes has decided to produce the game of
[Yatzy](https://en.wikipedia.org/wiki/Yatzy) (a public domain dice game similar
to [Yahtzee®](https://en.wikipedia.org/wiki/Yahtzee)). After a particularly
unfortunate incident involving dice, a slingshot, and a mug full of very hot
coffee, the company elected to produce a computerized version of the game
instead.
Look over the starter code, view [this short demonstration](TODO) and run the
program to get a feel for how the program works.
### Architecture
The starter code has a Model-View-Controller (MVC) architecture. Review this
architectural style in *Software Engineering* pp162-163 and *Engineering
Software Products* pp104-105. As you add to the starter code, preserve the MVC
architecture.
- Code that displays information to the user must be in the *view* subsystem.
- In this particular MVC variant, input from the user is obtained through
the view subsystem before being passed to the controller subsystem.
This design choice was made because the original version of this code
supports multiple user interfaces. For simplicity, *this* version
supports only a text-based user interface, and the remaining
inheritance hierarchy was collapsed, except for `AbstractDieView`.
- Code that coordinates the game is in the *controller* subsystem.
- Code that represents problem-domain concepts is in the *model* subsystem.
This code does not need to exactly mirror the problem-domain concepts, but
this is where the game state belongs.
With very limited exceptions, code in one subsystem is not allowed to depend on
concrete classes in another subsystem. The limited exceptions are:
- In the starter code, some classes in the controller subsystem instantiate
concrete classes as part of initializing the game. Outside of these
instantiations, all code that references objects from a different subsystem
do so using interfaces.
- There is a way to eliminate even these dependences, but that is a
design pattern for another day
- Your code may reference constants declared in the `Game` class, any public
`Enum` types, any code within the same subsystem, and interfaces in other
subsystems.
### Functional Requirements
The [Wikipedia page for Yatzy](https://en.wikipedia.org/wiki/Yatzy) contains
the description of the game. You will implement a one-player version.
- There are five dice. In each turn, the player may roll some or all of the
dice up to three times.
- After the first or second roll, the player *may* score the dice against a
scoring category and end the turn. After the third roll, the player *must*
score the dice against a scoring category and end the turn.
- The scoring categories are generally grouped into the "Upper Section" and
the "Lower Section"; however, due to the display's aspect ratio, we will
refer to them as the "Left Section" and the "Right Section."
- The Left Section has these scoring categories:
- Ones: The sum of all dice showing 1 pip
- Twos: The sum of all dice showing 2 pips
- Threes: The sum of all dice showing 3 pips
- Fours: The sum of all dice showing 4 pips
- Fives: The sum of all dice showing 5 pips
- Sixes: The sum of all dice showing 6 pips
- Bonus (not player-selectable): if the Left Section's player-
selectable categories sum to 63 (an average of three dice in each
category), then the player receives a 50-point bonus
- The Right Section has these scoring categories:
- One Pair (2 dice showing the same number of pips), scored as the
sum of those 2 dice
- Two Pair (2 different pairs of dice), scored as the sum of the dice
in those pairs
- The face-value of the two different pairs *must* be different;
for example, 1-2-2-3-3 may be scored as Two Pair, but 1-2-2-2-2
may not be scored as Two Pair
- Three of a Kind (3 dice showing the same number of pips), scored as
the sum of those 3 dice
- Four of a Kind (4 dice showing the same number of pips), scored as
the sum of those 4 dice
- Small Straight (the combination of 1-2-3-4-5), scored as the sum of
those 5 dice
- Note that this is *not* the same definition of a "small
straight" in Yahtzee®
- Large Straight (the combination of 2-3-4-5-6), scored as the sum of
those 5 dice
- Note that this is *not* the same definition of a "large
straight" in Yahtzee®
- Full House (three of a kind and a pair), scored as the sum of those
5 dice
- The face-value of the pair *must* be different than the face-
value of the three of a kind; for example, 2-2-2-3-3 may be
scored as a Full House, but 2-2-2-2-2 may not be scored as Full
House
- Chance (any combination of dice), scored as the sum of those 5 dice
- Yatzy (5 dice showing the same number of pips), scored as 50 points
- Dice that do not conform to a scoring category's requirements may be
assigned to that category, but they will receive a score of 0.
### Observer Pattern
You will use the *Observer Pattern* to update the view when the dice are rolled.
- HFDP, [Chapter 2](https://learning.oreilly.com/library/view/head-first-design/0596007124/ch02.html)
Because Java's implementation of the Observer Pattern is deprecated, we will
use our own implementation.
1. The methods in `AbstractSubject` currently do nothing. Implement the
`registerObserver`, `removeObserver`, and `notifyObservers` methods to
provide the behavior required of the subject in the Observer Pattern.
Now make use of the Observer Pattern. The starter code already has the
registration calls; you need to:
2. In the starter code, the `rollDice` method in `controller/DiceController`
updates the view with each die's new value. Locate and delete this line:
```
dieTuple.view.setValue(dieTuple.model.getValue());
```
1. Update the `roll` method in `model/die/Die`. Because this is the method
where something interesting happens, it needs to call the `notifyObservers`
method.
1. The `view/AbstractDieView` is an observer. Implement the `update` method in
`AbstractDieView`.
1. Run the game to confirm that the dice that are displayed update when they
are rolled.
### Template Method Pattern
You will use the *Template Method Pattern* to obtain the scoring categories'
scores.
- HFDP, [Chapter 8](https://learning.oreilly.com/library/view/head-first-design/0596007124/ch08.html)
The `model/scoring/AbstractDieBasedCategory` class is the base class for the
scoring categories that use dice to determine the score (as opposed to the
scoring categories that use the sums of other scoring categories to
determine their scores). Unlike Yahtzee®, almost all of the die-based scores
are the sums of a subset of the dice. This means that the
`getHypotheticalScore` can be implemented in `AbsttractDieBasedCategory` if it
had a way to get the dice that should be added together.
5. Create a
`protected abstract List<DieModel> getSatisfyingDice(List<DieModel> dice)`
method in `AbstractDieBasedCategory`.
1. Implement `getHypotheticalScore` in `AbstractDieBasedCategory` to use
`getSatisfyingDice` to produce the score that the dice would yield, if the
dice were assigned to the scoring category.
1. Implement the methods in `AbstractMatchingCategory`, `NumberCategory`,
`OfAKindCategory`, `OfTwoKindsCategory`, `SequenceCategory`, and
`ChanceCategory`.
- Except for `OfAKindCategory`, do *not* override `getHypotheticalScore`
in the subclasses.
- You do need to override `getHypotheticalScore` in `OfAKindCategory`
because a "Yatzy" (five of a kind) is scored as 50 points instead of as
the sum of the dice. Override `getHypotheticalScore` and use
`super.getHypotheticalScore` for the cases that are not "Yatzy."
**NOTE** The `model/scoring/MatchingCategoryTest` test class can be used to
test your implementations of `OfAKindCategory.isSimpleMatch` and
`OfTwoKindsCategory.isCompoundMatch`. The `model/scoring/CategoryModelTest`
test class can be used to test the full implementations of each of
`AbstractDieBasedCategory`'s subclasses.
### Command Pattern
You will use the *Command Pattern* to respond to user input.
- HFDP, [Chapter 6](https://learning.oreilly.com/library/view/head-first-design/0596007124/ch06.html)
8. Examine `controller/Command`.
- Notice that it declares an `execute()` method, just like HFDP shows.
- Notice that it also declares `toString()`. Even though this method is
already declared as part of Java's `Object` base class, we re-declare
it in `Command` to instruct implementers what `toString()` should
return.
1. Examine `DiceController`, `NavigationController` and `ScoreController`.
Notice that their constructors (and helper methods) instantiate `Command`
objects and pass them to the game board.
1. Examine the `playGame` method in `view/textview/TextGameBoard`.
Specifically, look at the two lines in the `try` block. **Observe that the
code in `TextGameBoard` has no knowledge of what these commands are and
what they do.** This code is decoupled from what actions the user can take:
Changing what options are available to the user will require no change to
any code in the view subsystem.
1. Examine `controller/navigation/RollDiceCommand` and other `Command` classes
to get a sense of how much logic is appropriate for the `execute` method.
1. The `addLeftCategories` method in `ScoreController` creates only the "Ones"
category. Modify it to create the "Twos," "Threes," "Fours," "Fives," and
"Sixes" categories.
1. Modify `model/scoring/BonusCategory`'s constructor so that it expects all
six scoring categories instead of just one.
1. The `Command` classes for the die-based scoring categories would all have
identical code in their `execute` methods (as well as other methods), and
so we have created `controller/scoring/AbstractDieBasedScoringCommand`.
Implement the `execute` method in `AbstractDieBasedScoringCommand`.
1. Run the game to check that you can now assign scores and that the scores
are reflected on the scoresheet.
1. Implement additional die-based scoring commands for the missing Right-Side
scoring categories.
1. Modify the `addRightCategories` method in `ScoreController` to create the
missing Right-Side categories.
1. Run the game to check that all scoring categories are displayed.
---
## Deliverables
For grading, we will pull the `yatzy` repositories after the assignment
is due, and we will look in the Maven-conventional directories for:
- Source code for your Yatzy game
*It is your responsibility to ensure that your work is in the master branch of
the **correct repository** and that we have the correct level of access to the
repository at the **time the assignment is due**. We will grade what we can
retrieve from the repository at the time it is due. Any work that is not in
the correct repository, or that we cannot access, will not be graded.*
## Rubric
The assignment is worth **25 points**:
- **4 points** for preserving the MVC architecture
- Code that contains the game state is in the model
- Code that interacts with the user is in the view
- Code that coordinates the game is in the controller
- Code in one subsystem does not depend on concrete classes in other
subsystems (exception: you may *instantiate* concrete CategoryModel
classes in the Controller classes' constructors)
- **5 points** for implementing and using the Observer Pattern
- `AbstractSubject` implemented
- `Die.roll` calls `notifyObservers`
- `AbstractDieView.update` implemented
- No code (other than the view) explicitly updates the view
- **4 points** for implementing and using the Template Method Pattern
- `AbstractDieBasedCategory.getHypotheticalScore` uses protected abstract
method as part of computing the score
- That method is overridden in each subclass as appropriate
- `getHypotheticalScore` is *not* overridden in any subclasses except for
the Yatzy special case
- **5 points** for implementing and using the Command Pattern
- `AbstractDieBasedScoringCommand.execute` implemented, delegating its
behavior
- All die-based scoring Commands implemented
- All scoring categories added to game board
- **4 points** for the game meeting all of its functional requirements
- **1 point** for making regular commits throughout the project
- **2 points** for meaningful commit messages
This assignment is scoped for a team of 2 students. If, despite your attempts
to engage your partner, your partner does not contribute to the assignment then
we will take that into account when grading.
*If **at any time** your repository is public or has internal visibility then
you will receive a 10% penalty. Further, if another student accesses your
non-private repository and copies your solution then I will assume that you are
complicit in their academic dishonesty.*
## Contribution Rubric
The contribution is worth **10 points**:
- **1 point** for completing peer assessment on time
- **1 point** for contacting your partner promptly
- **4 points** for equitable contribution based on peer assessments
- **4 points** for equitable contribution based on git history
## Footnote
...@@ -4,8 +4,8 @@ ...@@ -4,8 +4,8 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>edu.unl.cse.csce361</groupId> <groupId>edu.unl.cse.csce361.yatzy</groupId>
<artifactId>project_name</artifactId> <artifactId>yatzy</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.0-SNAPSHOT</version>
<build> <build>
...@@ -14,21 +14,18 @@ ...@@ -14,21 +14,18 @@
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version> <version>3.8.1</version>
<configuration> <configuration>
<source>8</source> <source>11</source>
<target>8</target> <target>11</target>
</configuration> </configuration>
</plugin> </plugin>
<!-- UNCOMMENT THIS SECTION IF YOU'RE USING JAVAFX, AND BE SURE TO SET THE MAINCLASS! -->
<!--
<plugin> <plugin>
<groupId>org.openjfx</groupId> <groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId> <artifactId>javafx-maven-plugin</artifactId>
<version>0.0.4</version> <version>0.0.4</version>
<configuration> <configuration>
<mainClass>edu.unl.cse.csce361.project_name.Main</mainClass> <mainClass>edu.unl.cse.csce361.yatzy.Main</mainClass>
</configuration> </configuration>
</plugin> </plugin>
-->
</plugins> </plugins>
</build> </build>
...@@ -36,66 +33,38 @@ ...@@ -36,66 +33,38 @@
<!-- FOR PRODUCTION CODE --> <!-- FOR PRODUCTION CODE -->
<!-- UNCOMMENT THIS SECTION IF YOU'RE USING MYSQL (EITHER WITH HIBERNATE OR JDBC --> <!-- https://mvnrepository.com/artifact/com.ibm.icu/icu4j -->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency> <dependency>
<groupId>mysql</groupId> <groupId>com.ibm.icu</groupId>
<artifactId>mysql-connector-java</artifactId> <artifactId>icu4j</artifactId>
<version>8.0.19</version> <version>67.1</version>
</dependency> </dependency>
<!-- UNCOMMENT THE NEXT THREE SECTIONS IF YOU'RE USING HIBERNATE -->
<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-core --> <!-- https://mvnrepository.com/artifact/com.googlecode.json-simple/json-simple -->
<!--
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.12.Final</version>
</dependency>
-->
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<!--
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
-->
<!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 -->
<!--
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>com.googlecode.json-simple</groupId>
<artifactId>slf4j-log4j12</artifactId> <artifactId>json-simple</artifactId>
<version>1.7.30</version> <version>1.1.1</version>
<scope>test</scope>
</dependency> </dependency>
-->
<!-- UNCOMMENT THE NEXT THREE SECTIONS IF YOU'RE USING JAVAFX -->
<!-- https://mvnrepository.com/artifact/org.openjfx/javafx-fxml --> <!-- https://mvnrepository.com/artifact/org.openjfx/javafx-fxml -->
<!--
<dependency> <dependency>
<groupId>org.openjfx</groupId> <groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId> <artifactId>javafx-fxml</artifactId>
<version>11</version> <version>11</version>
</dependency> </dependency>
-->
<!-- https://mvnrepository.com/artifact/org.openjfx/javafx-base --> <!-- https://mvnrepository.com/artifact/org.openjfx/javafx-base -->
<!--
<dependency> <dependency>
<groupId>org.openjfx</groupId> <groupId>org.openjfx</groupId>
<artifactId>javafx-base</artifactId> <artifactId>javafx-base</artifactId>
<version>11</version> <version>11</version>
</dependency> </dependency>
-->
<!-- https://mvnrepository.com/artifact/org.openjfx/javafx-controls --> <!-- https://mvnrepository.com/artifact/org.openjfx/javafx-controls -->
<!--
<dependency> <dependency>
<groupId>org.openjfx</groupId> <groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId> <artifactId>javafx-controls</artifactId>
<version>11</version> <version>11</version>
</dependency> </dependency>
-->
<!-- FOR TEST CODE --> <!-- FOR TEST CODE -->
...@@ -106,16 +75,6 @@ ...@@ -106,16 +75,6 @@
<version>4.13</version> <version>4.13</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- UNCOMMENT THIS SECTION IF YOU'RE USING HYPERSQL FOR IN-MEMORY DATABASE TESTING -->
<!-- https://mvnrepository.com/artifact/org.hsqldb/hsqldb -->
<!--
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.5.0</version>
<scope>test</scope>
</dependency>
-->
</dependencies> </dependencies>
</project> </project>
package edu.unl.cse.csce361.yatzy;
import java.util.HashSet;
import java.util.Set;
/**
* Provides the subject (or observable) behavior for the Observer Pattern. Concrete subject classes may inherit from
* AbstractSubject or may instantiate an inner-class extension to act as a delegate.
*/
public abstract class AbstractSubject implements Subject {
/**
* The collection of observers to be notified of updates to the subject.
*/
private final Set<Observer> observers;
protected AbstractSubject() {
observers = new HashSet<>();
}
/**
* Calls the {@link Observer#update(Subject)} method on each observer in {@link #observers}, providing a link to
* <code>this</code>, the object that has updated. If the observer needs the subject's state or a description of
* what changed, it is the observer's responsibility to do so.
*/
protected void notifyObservers() {
;
}
@Override
public void registerObserver(Observer observer) {
;
}
@Override
public void removeObserver(Observer observer) {
;
}
}
package edu.unl.cse.csce361.yatzy;
/**
* A specialized subject in the Observer Pattern. DieSubject provides a mechanism for observers to determine the
* subject's face value. We expect the subject to represent a die (in the problem domain sense), though it need not
* be an object of any particular class.
*/
public interface DieSubject extends Subject {
/**
* Provides the die's face value.
*
* @return a value between 1 and {@link Game#NUMBER_OF_DIE_SIDES}, inclusive
*/
int getValue();
}
package edu.unl.cse.csce361.yatzy;
import edu.unl.cse.csce361.yatzy.controller.Controller;
import edu.unl.cse.csce361.yatzy.controller.DiceController;
import edu.unl.cse.csce361.yatzy.controller.NavigationController;
import edu.unl.cse.csce361.yatzy.controller.ScoreController;
import edu.unl.cse.csce361.yatzy.view.GameBoard;
import edu.unl.cse.csce361.yatzy.view.textview.TextGameBoard;
/**
* <p>The main class for the game of Yatzy.</p>
*
* <p>If running from a terminal window that emulates a VT100 terminal then you can use <code>VT100</code> as a
* command-line argument to cause the cursor to move when refreshing instead of scrolling the screen.</p>
*/
public class Game {
public static final int NUMBER_OF_DICE = 5;
public static final int NUMBER_OF_DIE_SIDES = 6;
public static void main(String[] args) {
boolean isVT100 = ((args.length > 0) && args[0].equalsIgnoreCase("vt100"));
GameBoard board = new TextGameBoard(isVT100);
Controller.setBoard(board);
DiceController.getController();
ScoreController.getController();
NavigationController.getController().startGame();
}
}
package edu.unl.cse.csce361.yatzy;
/**
* The observer in the Observer Pattern.
*/
public interface Observer {
/**
* The method by which the observer is notified of updates to the subject. Typically called by the subject itself,
* with <code>this</code> as the argument.
*
* @param subject The subject, or observable, that has been updated.
*/
void update(Subject subject);
}
package edu.unl.cse.csce361.yatzy;
/**
* The subject, or observable, in the Observer Pattern.
*/
public interface Subject {
/**
* Adds an observer to the collection of observers to be notified when the subject has an update. If the observer
* is already registered, there is no change. While the Observer Pattern is usually described as having observers
* register themselves, there is no reason that another object cannot register the observer.
*
* @param observer The observer to be added
*/
void registerObserver(Observer observer);
/**
* Remove's an observer from the collection of observers that are notified when the subject has an update. If the
* observer is not registered, there is no change.
*
* @param observer The observer to be removed.
*/
void removeObserver(Observer observer);
}
package edu.unl.cse.csce361.yatzy.controller;
/**
* Base interface for the Command Pattern.
*/
public interface Command {
/**
* <p>The method to be called whenever the user selects a command.</p>
* <p>In general, implementations should delegate the behavior to another object.</p>
*/
default void execute() {
System.err.println(this.getClass().getName() + " not yet implemented.");
}
/**
* Implementations should override {@link Object#toString()} so a meaningful description of the command can be
* shown to the user.
*
* @return the text to be displayed on the user interface
*/
String toString();
}
package edu.unl.cse.csce361.yatzy.controller;
import edu.unl.cse.csce361.yatzy.Game;
import edu.unl.cse.csce361.yatzy.model.die.Die;
import edu.unl.cse.csce361.yatzy.model.DieModel;
import edu.unl.cse.csce361.yatzy.view.GameBoard;
import java.util.List;
/**
* Base class for all Controller classes, responsible for initializing references used throughout the controller
* subsystem.
*/
public abstract class Controller {
protected static GameBoard board = null;
protected static final List<DieModel> dice;
static {
Die.setNumberOfSides(Game.NUMBER_OF_DIE_SIDES);
dice = List.of(new Die(), new Die(), new Die(), new Die(), new Die());
}
public static void setBoard(GameBoard gameBoard) {
board = gameBoard;
}
}
package edu.unl.cse.csce361.yatzy.controller;
import edu.unl.cse.csce361.yatzy.Game;
import edu.unl.cse.csce361.yatzy.controller.die.DieSelectionCommand;
import edu.unl.cse.csce361.yatzy.model.DieModel;
import edu.unl.cse.csce361.yatzy.view.DieView;
import edu.unl.cse.csce361.yatzy.view.textview.TextDieView;
import java.util.HashMap;
import java.util.Map;
/**
* Controller for coordinating actions involving dice.
*/
public class DiceController extends Controller {
public static final int MAXIMUM_NUMBER_OF_ROLLS = 3;
private static DiceController instance = null;
public static DiceController getController() {
if (board == null) {
throw new IllegalStateException("Need to set Controller.board before creating the instance.");
}
if (instance == null) {
instance = new DiceController();
}
return instance;
}
private final Map<DieCommand, DieTuple> dieTuples;
private int rollNumber = 0;
private DiceController() {
super();
dieTuples = new HashMap<>(Game.NUMBER_OF_DICE);
dice.forEach((die) -> this.addDie(die, new TextDieView(), new DieSelectionCommand()));
}
private void addDie(DieModel dieModel, DieView dieView, DieCommand dieCommand) {
dieTuples.put(dieCommand, new DieTuple(dieModel, dieView, dieCommand));
board.addCommand(dieCommand, dieView);
dieModel.registerObserver(dieView);
}
/**
* Gets the die model associated with a die command
*
* @param command a die command associated with a die model and a die view
* @return the die model
*/
public DieModel getModel(DieCommand command) {
return dieTuples.get(command).model;
}
/**
* Gets the die view associated with a die command
*
* @param command a die command associated with a die model and a die view
* @return the die view
*/
public DieView getView(DieCommand command) {
return dieTuples.get(command).view;
}
/**
* Rolls the dice. Specifically, for each problem-domain die, if the view is not highlighted then the model is
* instructed to take on an in-range random value. If the die's view is highlighted then the die's model is
* unchanged.
*/
public void rollDice() {
rollNumber++;
for (DieTuple dieTuple : dieTuples.values()) {
if (dieTuple.view.isHighlighted()) {
dieTuple.view.unHighlight();
} else {
dieTuple.model.roll();
dieTuple.view.setValue(dieTuple.model.getValue());
}
}
if (rollNumber == MAXIMUM_NUMBER_OF_ROLLS) {
NavigationController.getController().setScoringPhase();
}
}
/**
* At the start of each turn, the player has {@link #MAXIMUM_NUMBER_OF_ROLLS} rolls remaining; this method resets
* the count from the previous turn.
*/
public void resetRollNumber() {
rollNumber = 0;
}
private static class DieTuple {
public final DieModel model;
public final DieView view;
public final DieCommand command;
public DieTuple(DieModel dieModel, DieView dieView, DieCommand dieCommand) {
model = dieModel;
view = dieView;
command = dieCommand;
}
}
}
package edu.unl.cse.csce361.yatzy.controller;
/**
* Marker interface for die commands. While this interface does not add anything to the base interface, it provides
* type safety.
*/
public interface DieCommand extends Command {
}
package edu.unl.cse.csce361.yatzy.controller;
public enum GamePhase {READY, ROLLING, SCORING}
package edu.unl.cse.csce361.yatzy.controller;
/**
* Marker interface for navigation commands. While this interface makes navigation commands sortable, its principal
* purpose is to provide type safety.
*/
public interface NavigationCommand extends Command, Comparable<NavigationCommand> {
}
package edu.unl.cse.csce361.yatzy.controller;
import edu.unl.cse.csce361.yatzy.controller.navigation.EndGameCommand;
import edu.unl.cse.csce361.yatzy.controller.navigation.RollDiceCommand;
import edu.unl.cse.csce361.yatzy.controller.navigation.ScoreDiceCommand;
import edu.unl.cse.csce361.yatzy.view.GameBoard;
/**
* Controller for coordinating navigation through the game.
*/
public class NavigationController extends Controller {
private static NavigationController instance = null;
private GamePhase phase;
public static NavigationController getController() {
if (board == null) {
throw new IllegalStateException("Need to set Controller.board before creating the instance.");
}
if (instance == null) {
instance = new NavigationController();
}
return instance;
}
private NavigationCommand endGameCommand;
private NavigationCommand rollDiceCommand;
private NavigationCommand scoreDiceCommand;
private void createCommands() {
endGameCommand = new EndGameCommand();
rollDiceCommand = new RollDiceCommand();
scoreDiceCommand = new ScoreDiceCommand();
}
private NavigationController() {
super();
phase = GamePhase.READY;
createCommands();
addReadyCommands();
}
private void addReadyCommands() {
board.addCommand(endGameCommand);
board.addCommand(rollDiceCommand);
}
/**
* Sets the game into the "READY" phase, in which the player has not used any rolls for the current turn.
*/
public void setReadyPhase() {
board.deactivateScoreCommands();
if (!board.allScoresMade()) {
board.addCommand(rollDiceCommand);
}
DiceController.getController().resetRollNumber();
phase = GamePhase.READY;
}
/**
* Sets the game into the "ROLLING" phase, in which the player has started rolling dice for the current turn but has
* not yet exhausted their rolls.
*/
public void setRollingPhase() {
switch (phase) {
case READY:
board.activateDieCommands();
board.addCommand(scoreDiceCommand);
break;
case ROLLING:
/* do nothing unique */
break;
case SCORING:
board.activateDieCommands();
board.deactivateCommand(scoreDiceCommand);
board.deactivateScoreCommands();
break;
default:
throw new IllegalStateException("Reached unreachable code in NavigationController.setRollingPhase(). " +
"Phase=" + phase);
}
phase = GamePhase.ROLLING;
}
/**
* Sets the game into the "SCORING" phase, in which the player is no longer rolling dice for the current turn but is
* instead deciding which scoring category to apply the dice to.
*/
public void setScoringPhase() {
board.activateScoreCommands();
board.deactivateDieCommands();
board.deactivateCommand(rollDiceCommand);
board.deactivateCommand(scoreDiceCommand);
phase = GamePhase.SCORING;
}
/**
* Starts a Yatzy game.
*/
public void startGame() {
try {
board.playGame();
} catch (NullPointerException exception) {
System.err.println("An error occurred: " + exception + " at " + exception.getStackTrace()[0]);
}
}
/**
* Ends the game, terminating the program.
*/
public void endGame() {
board.endGame();
}
}
package edu.unl.cse.csce361.yatzy.controller;
import edu.unl.cse.csce361.yatzy.controller.scoring.AbstractDieBasedScoringCommand;
import edu.unl.cse.csce361.yatzy.controller.scoring.ChanceCommand;
import edu.unl.cse.csce361.yatzy.controller.scoring.NumberScoringCommand;
import edu.unl.cse.csce361.yatzy.controller.scoring.SumBasedScoringCommand;
import edu.unl.cse.csce361.yatzy.model.CategoryModel;
import edu.unl.cse.csce361.yatzy.model.DieModel;
import edu.unl.cse.csce361.yatzy.model.scoring.BonusCategory;
import edu.unl.cse.csce361.yatzy.model.scoring.TotalCategory;
import edu.unl.cse.csce361.yatzy.view.textview.TextGameBoard;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* Controller for coordinating the scoring of dice rolls.
*/
public class ScoreController extends Controller {
private static ScoreController instance = null;
public static ScoreController getController() {
if (board == null) {
throw new IllegalStateException("Need to set Controller.board before creating the instance.");
}
if (instance == null) {
instance = new ScoreController();
}
return instance;
}
private ScoreController() {
super();
List<CategoryModel> leftCategoryModels = addLeftCategories();
List<CategoryModel> rightCategoryModels = addRightCategories();
addSumCategories(leftCategoryModels, rightCategoryModels);
}
/**
* Applies dice to a scoring category. The dice are used to assign a score to the category's model, and the view
* is changed to show the new score and to reflect that the category has been scored.
*
* @param scoringCommand the scoring category's command
* @param categoryModel the scoring category's model
*/
public void assignScoreToCategory(ScoringCommand scoringCommand, CategoryModel categoryModel) {
categoryModel.assignScore(dice);
board.deactivateCommand(scoringCommand);
board.setMessage("Scored " + categoryModel.getScore() + " points on " + scoringCommand + ".");
dice.forEach(DieModel::reset);
}
private List<CategoryModel> addLeftCategories() {
List<CategoryModel> categoryModels = new LinkedList<>();
for (int i = 1; i <= 1; i++) {
NumberScoringCommand categoryCommand = new NumberScoringCommand(i);
categoryModels.add(categoryCommand.getCategoryModel());
board.addCommand(categoryCommand, TextGameBoard.CategorySide.NUMBER_SIDE);
}
return categoryModels;
}
private void addRightCategory(AbstractDieBasedScoringCommand categoryCommand, List<CategoryModel> categoryModels) {
categoryModels.add(categoryCommand.getCategoryModel());
board.addCommand(categoryCommand, TextGameBoard.CategorySide.COMBO_SIDE);
}
private List<CategoryModel> addRightCategories() {
List<CategoryModel> categoryModels = new LinkedList<>();
addRightCategory(new ChanceCommand(), categoryModels);
return categoryModels;
}
private void addSumCategories(List<CategoryModel> leftCategoryModels,
List<CategoryModel> rightCategoryModels) {
List<CategoryModel> sums = new ArrayList<>(3);
CategoryModel categoryModel = new TotalCategory(leftCategoryModels);
sums.add(categoryModel);
board.addSubtotalCommand(new SumBasedScoringCommand(categoryModel, "Subtotal"),
TextGameBoard.CategorySide.NUMBER_SIDE);
categoryModel = new BonusCategory(leftCategoryModels);
sums.add(categoryModel);
board.addSubtotalCommand(new SumBasedScoringCommand(categoryModel, "Bonus"),
TextGameBoard.CategorySide.NUMBER_SIDE);
categoryModel = new TotalCategory(rightCategoryModels);
sums.add(categoryModel);
board.addSubtotalCommand(new SumBasedScoringCommand(categoryModel, "Subtotal"),
TextGameBoard.CategorySide.COMBO_SIDE);
board.addGrandTotalCommand(new SumBasedScoringCommand(new TotalCategory(sums), "Grand Total"));
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment