mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-26 20:54:07 -08:00
Merge branch 'coding-horror:main' into main
This commit is contained in:
37
00_Utilities/jvmTestUtils/kotlin/test/ConsoleTest.kt
Normal file
37
00_Utilities/jvmTestUtils/kotlin/test/ConsoleTest.kt
Normal file
@@ -0,0 +1,37 @@
|
||||
package com.pcholt.console.testutils
|
||||
|
||||
import com.google.common.truth.Truth
|
||||
import org.junit.Rule
|
||||
import org.junit.contrib.java.lang.system.SystemOutRule
|
||||
import org.junit.contrib.java.lang.system.TextFromStandardInputStream
|
||||
|
||||
abstract class ConsoleTest {
|
||||
@get:Rule
|
||||
val inputRule = TextFromStandardInputStream.emptyStandardInputStream()
|
||||
|
||||
@get:Rule
|
||||
val systemOutRule = SystemOutRule().enableLog()
|
||||
|
||||
val regexInputCommand = "\\{(.*)}".toRegex()
|
||||
|
||||
fun assertConversation(conversation: String, runMain: () -> Unit) {
|
||||
|
||||
inputRule.provideLines(*regexInputCommand
|
||||
.findAll(conversation)
|
||||
.map { it.groupValues[1] }
|
||||
.toList().toTypedArray())
|
||||
|
||||
runMain()
|
||||
|
||||
Truth.assertThat(
|
||||
systemOutRule.log.trimWhiteSpace()
|
||||
)
|
||||
.isEqualTo(
|
||||
regexInputCommand
|
||||
.replace(conversation, "").trimWhiteSpace()
|
||||
)
|
||||
}
|
||||
|
||||
private fun String.trimWhiteSpace() =
|
||||
replace("[\\s]+".toRegex(), " ")
|
||||
}
|
||||
@@ -74,11 +74,11 @@ public class Animal {
|
||||
private static void askForInformationAndSave(Scanner scan, AnimalNode current, QuestionNode previous, boolean previousToCurrentDecisionChoice) {
|
||||
//Failed to get it right and ran out of questions
|
||||
//Let's ask the user for the new information
|
||||
System.out.print("THE ANIMAL YOU WERE THINKING OF WAS A ");
|
||||
System.out.print("THE ANIMAL YOU WERE THINKING OF WAS A ? ");
|
||||
String animal = scan.nextLine();
|
||||
System.out.printf("PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A %s FROM A %s ", animal, current.getAnimal());
|
||||
System.out.printf("PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A %s FROM A %s ? ", animal, current.getAnimal());
|
||||
String newQuestion = scan.nextLine();
|
||||
System.out.printf("FOR A %s THE ANSWER WOULD BE ", animal);
|
||||
System.out.printf("FOR A %s THE ANSWER WOULD BE ? ", animal);
|
||||
boolean newAnswer = readYesOrNo(scan);
|
||||
//Add it to our question store
|
||||
addNewAnimal(current, previous, animal, newQuestion, newAnswer, previousToCurrentDecisionChoice);
|
||||
55
03_Animal/java/test/AnimalJavaTest.kt
Normal file
55
03_Animal/java/test/AnimalJavaTest.kt
Normal file
@@ -0,0 +1,55 @@
|
||||
import com.pcholt.console.testutils.ConsoleTest
|
||||
import org.junit.Test
|
||||
|
||||
class AnimalJavaTest : ConsoleTest() {
|
||||
|
||||
@Test
|
||||
fun `given a standard setup, find the fish`() {
|
||||
assertConversation(
|
||||
"""
|
||||
$title
|
||||
ARE YOU THINKING OF AN ANIMAL ? {YES}
|
||||
DOES IT SWIM ? {YES}
|
||||
IS IT A FISH ? {YES}
|
||||
WHY NOT TRY ANOTHER ANIMAL?
|
||||
ARE YOU THINKING OF AN ANIMAL ? {QUIT}
|
||||
"""
|
||||
) {
|
||||
Animal.main(emptyArray())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a standard setup, create a cow, and verify`() {
|
||||
assertConversation(
|
||||
"""
|
||||
$title
|
||||
ARE YOU THINKING OF AN ANIMAL ? {YES}
|
||||
DOES IT SWIM ? {NO}
|
||||
IS IT A BIRD ? {NO}
|
||||
THE ANIMAL YOU WERE THINKING OF WAS A ? {COW}
|
||||
PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A
|
||||
COW FROM A BIRD
|
||||
? {DOES IT EAT GRASS}
|
||||
FOR A COW THE ANSWER WOULD BE ? {YES}
|
||||
ARE YOU THINKING OF AN ANIMAL ? {YES}
|
||||
DOES IT SWIM ? {NO}
|
||||
DOES IT EAT GRASS ? {YES}
|
||||
IS IT A COW ? {YES}
|
||||
WHY NOT TRY ANOTHER ANIMAL?
|
||||
ARE YOU THINKING OF AN ANIMAL ? {QUIT}
|
||||
"""
|
||||
) {
|
||||
Animal.main(emptyArray())
|
||||
}
|
||||
}
|
||||
|
||||
private val title = """
|
||||
ANIMAL
|
||||
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
|
||||
|
||||
PLAY 'GUESS THE ANIMAL'
|
||||
THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.
|
||||
"""
|
||||
}
|
||||
|
||||
55
03_Animal/kotlin/test/AnimalKtTest.kt
Normal file
55
03_Animal/kotlin/test/AnimalKtTest.kt
Normal file
@@ -0,0 +1,55 @@
|
||||
import com.pcholt.console.testutils.ConsoleTest
|
||||
import org.junit.Test
|
||||
|
||||
class AnimalKtTest : ConsoleTest() {
|
||||
|
||||
@Test
|
||||
fun `given a standard setup, find the fish`() {
|
||||
assertConversation(
|
||||
"""
|
||||
$title
|
||||
ARE YOU THINKING OF AN ANIMAL? {YES}
|
||||
DOES IT SWIM? {YES}
|
||||
IS IT A FISH? {YES}
|
||||
WHY NOT TRY ANOTHER ANIMAL?
|
||||
ARE YOU THINKING OF AN ANIMAL? {QUIT}
|
||||
"""
|
||||
) {
|
||||
main()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a standard setup, create a cow, and verify`() {
|
||||
assertConversation(
|
||||
"""
|
||||
$title
|
||||
ARE YOU THINKING OF AN ANIMAL? {YES}
|
||||
DOES IT SWIM? {NO}
|
||||
IS IT A BIRD? {NO}
|
||||
THE ANIMAL YOU WERE THINKING OF WAS A? {COW}
|
||||
PLEASE TYPE IN A QUESTION THAT WOULD DISTINGUISH A
|
||||
COW FROM A BIRD
|
||||
? {DOES IT EAT GRASS}
|
||||
FOR A COW THE ANSWER WOULD BE? {YES}
|
||||
ARE YOU THINKING OF AN ANIMAL? {YES}
|
||||
DOES IT SWIM? {NO}
|
||||
DOES IT EAT GRASS? {YES}
|
||||
IS IT A COW? {YES}
|
||||
WHY NOT TRY ANOTHER ANIMAL?
|
||||
ARE YOU THINKING OF AN ANIMAL? {QUIT}
|
||||
"""
|
||||
) {
|
||||
main()
|
||||
}
|
||||
}
|
||||
|
||||
private val title = """
|
||||
ANIMAL
|
||||
CREATIVE COMPUTING MORRISTOWN, NEW JERSEY
|
||||
|
||||
PLAY 'GUESS THE ANIMAL'
|
||||
THINK OF AN ANIMAL AND THE COMPUTER WILL TRY TO GUESS IT.
|
||||
"""
|
||||
}
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
# Game of Life - Java version
|
||||
|
||||
Original source downloaded [from Vintage Basic](http://www.vintage-basic.net/games.html)
|
||||
|
||||
Conversion to [Oracle Java](https://openjdk.java.net/)
|
||||
|
||||
## Requirements
|
||||
|
||||
* Requires Java 17 (or later)
|
||||
|
||||
## Notes
|
||||
|
||||
The Java version of Game of Life tries to mimics the behaviour of the BASIC version.
|
||||
However, the Java code does not have much in common with the original.
|
||||
|
||||
**Differences in behaviour:**
|
||||
* Input supports the ```.``` character, but it's optional.
|
||||
* Evaluation of ```DONE``` input string is case insensitive.
|
||||
* Run with the ```-s``` command line argument to halt the program after each generation, and continue when ```ENTER``` is pressed.
|
||||
191
55_Life/java/src/java/Life.java
Normal file
191
55_Life/java/src/java/Life.java
Normal file
@@ -0,0 +1,191 @@
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* The Game of Life class.<br>
|
||||
* <br>
|
||||
* Mimics the behaviour of the BASIC version, however the Java code does not have much in common with the original.
|
||||
* <br>
|
||||
* Differences in behaviour:
|
||||
* <ul>
|
||||
* <li>Input supports the "." character, but it's optional.</li>
|
||||
* <li>Input regarding the "DONE" string is case insensitive.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public class Life {
|
||||
|
||||
private static final byte DEAD = 0;
|
||||
private static final byte ALIVE = 1;
|
||||
private static final String NEWLINE = "\n";
|
||||
|
||||
private final Scanner consoleReader = new Scanner(System.in);
|
||||
|
||||
private final byte[][] matrix = new byte[21][67];
|
||||
private int generation = 0;
|
||||
private int population = 0;
|
||||
boolean stopAfterGen = false;
|
||||
boolean invalid = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public Life(String[] args) {
|
||||
parse(args);
|
||||
}
|
||||
|
||||
private void parse(String[] args) {
|
||||
for (String arg : args) {
|
||||
if ("-s".equals(arg)) {
|
||||
stopAfterGen = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the game.
|
||||
*/
|
||||
public void start() {
|
||||
printGameHeader();
|
||||
readPattern();
|
||||
while (true) {
|
||||
printGeneration();
|
||||
advanceToNextGeneration();
|
||||
if (stopAfterGen) {
|
||||
System.out.print("PRESS ENTER TO CONTINUE");
|
||||
consoleReader.nextLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void advanceToNextGeneration() {
|
||||
// store all cell transitions in a list, i.e. if a dead cell becomes alive, or a living cell dies
|
||||
List<Transition> transitions = new ArrayList<>();
|
||||
// there's still room for optimization: instead of iterating over all cells in the matrix,
|
||||
// we could consider only the section containing the pattern(s), as in the BASIC version
|
||||
for (int y = 0; y < matrix.length; y++) {
|
||||
for (int x = 0; x < matrix[y].length; x++) {
|
||||
int neighbours = countNeighbours(y, x);
|
||||
if (matrix[y][x] == ALIVE) {
|
||||
if (neighbours < 2 || neighbours > 3) {
|
||||
transitions.add(new Transition(y, x, DEAD));
|
||||
population--;
|
||||
}
|
||||
} else { // cell is dead
|
||||
if (neighbours == 3) {
|
||||
if (x < 2 || x > 67 || y < 2 || y > 21) {
|
||||
invalid = true;
|
||||
}
|
||||
transitions.add(new Transition(y, x, ALIVE));
|
||||
population++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// apply all transitions to the matrix
|
||||
transitions.forEach(t -> matrix[t.y()][t.x()] = t.newState());
|
||||
generation++;
|
||||
}
|
||||
|
||||
private int countNeighbours(int y, int x) {
|
||||
int neighbours = 0;
|
||||
for (int row = Math.max(y - 1, 0); row <= Math.min(y + 1, matrix.length - 1); row++) {
|
||||
for (int col = Math.max(x - 1, 0); col <= Math.min(x + 1, matrix[row].length - 1); col++) {
|
||||
if (row == y && col == x) {
|
||||
continue;
|
||||
}
|
||||
if (matrix[row][col] == ALIVE) {
|
||||
neighbours++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return neighbours;
|
||||
}
|
||||
|
||||
private void readPattern() {
|
||||
System.out.println("ENTER YOUR PATTERN:");
|
||||
List<String> lines = new ArrayList<>();
|
||||
String line;
|
||||
int maxLineLength = 0;
|
||||
boolean reading = true;
|
||||
while (reading) {
|
||||
System.out.print("? ");
|
||||
line = consoleReader.nextLine();
|
||||
if (line.equalsIgnoreCase("done")) {
|
||||
reading = false;
|
||||
} else {
|
||||
// optional support for the '.' that is needed in the BASIC version
|
||||
lines.add(line.replace('.', ' '));
|
||||
maxLineLength = Math.max(maxLineLength, line.length());
|
||||
}
|
||||
}
|
||||
fillMatrix(lines, maxLineLength);
|
||||
}
|
||||
|
||||
private void fillMatrix(List<String> lines, int maxLineLength) {
|
||||
float xMin = 33 - maxLineLength / 2f;
|
||||
float yMin = 11 - lines.size() / 2f;
|
||||
for (int y = 0; y < lines.size(); y++) {
|
||||
String line = lines.get(y);
|
||||
for (int x = 1; x <= line.length(); x++) {
|
||||
if (line.charAt(x-1) == '*') {
|
||||
matrix[floor(yMin + y)][floor(xMin + x)] = ALIVE;
|
||||
population++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int floor(float f) {
|
||||
return (int) Math.floor(f);
|
||||
}
|
||||
|
||||
private void printGameHeader() {
|
||||
printIndented(34, "LIFE");
|
||||
printIndented(15, "CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
|
||||
System.out.println(NEWLINE.repeat(3));
|
||||
}
|
||||
|
||||
private void printIndented(int spaces, String str) {
|
||||
System.out.println(" ".repeat(spaces) + str);
|
||||
}
|
||||
|
||||
private void printGeneration() {
|
||||
printGenerationHeader();
|
||||
for (int y = 0; y < matrix.length; y++) {
|
||||
for (int x = 0; x < matrix[y].length; x++) {
|
||||
System.out.print(matrix[y][x] == 1 ? "*" : " ");
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
private void printGenerationHeader() {
|
||||
String invalidText = invalid ? "INVALID!" : "";
|
||||
System.out.printf("GENERATION: %-13d POPULATION: %d %s\n", generation, population, invalidText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Main method that starts the program.
|
||||
*
|
||||
* @param args the command line arguments:
|
||||
* <pre>-s: Stop after each generation (press enter to continue)</pre>
|
||||
* @throws Exception if something goes wrong.
|
||||
*/
|
||||
public static void main(String[] args) throws Exception {
|
||||
new Life(args).start();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a state change for a single cell within the matrix.
|
||||
*
|
||||
* @param y the y coordinate (row) of the cell
|
||||
* @param x the x coordinate (column) of the cell
|
||||
* @param newState the new state of the cell (either DEAD or ALIVE)
|
||||
*/
|
||||
record Transition(int y, int x, byte newState) { }
|
||||
149
85_Synonym/csharp/Synonym.cs
Normal file
149
85_Synonym/csharp/Synonym.cs
Normal file
@@ -0,0 +1,149 @@
|
||||
using System.Text;
|
||||
|
||||
namespace Synonym
|
||||
{
|
||||
class Synonym
|
||||
{
|
||||
Random rand = new Random();
|
||||
|
||||
// Initialize list of corrent responses
|
||||
private string[] Affirmations = { "Right", "Correct", "Fine", "Good!", "Check" };
|
||||
|
||||
// Initialize list of words and their synonyms
|
||||
private string[][] Words =
|
||||
{
|
||||
new string[] {"first", "start", "beginning", "onset", "initial"},
|
||||
new string[] {"similar", "alike", "same", "like", "resembling"},
|
||||
new string[] {"model", "pattern", "prototype", "standard", "criterion"},
|
||||
new string[] {"small", "insignificant", "little", "tiny", "minute"},
|
||||
new string[] {"stop", "halt", "stay", "arrest", "check", "standstill"},
|
||||
new string[] {"house", "dwelling", "residence", "domicile", "lodging", "habitation"},
|
||||
new string[] {"pit", "hole", "hollow", "well", "gulf", "chasm", "abyss"},
|
||||
new string[] {"push", "shove", "thrust", "prod", "poke", "butt", "press"},
|
||||
new string[] {"red", "rouge", "scarlet", "crimson", "flame", "ruby"},
|
||||
new string[] {"pain", "suffering", "hurt", "misery", "distress", "ache", "discomfort"}
|
||||
};
|
||||
|
||||
private void DisplayIntro()
|
||||
{
|
||||
Console.WriteLine("");
|
||||
Console.WriteLine("SYNONYM".PadLeft(23));
|
||||
Console.WriteLine("CREATIVE COMPUTING MORRISTOWN, NEW JERSEY");
|
||||
Console.WriteLine("");
|
||||
Console.WriteLine("A synonym of a word means another word in the English");
|
||||
Console.WriteLine("language which has the same or very nearly the same meaning.");
|
||||
Console.WriteLine("I choose a word -- you type a synonym.");
|
||||
Console.WriteLine("If you can't think of a synonym, type the word 'help'");
|
||||
Console.WriteLine("and I will tell you a synonym.");
|
||||
Console.WriteLine("");
|
||||
}
|
||||
|
||||
private void DisplayOutro()
|
||||
{
|
||||
Console.WriteLine("Synonym drill completed.");
|
||||
}
|
||||
|
||||
private void RandomizeTheList()
|
||||
{
|
||||
// Randomize the list of Words to pick from
|
||||
int[] Order = new int[Words.Length];
|
||||
foreach (int i in Order)
|
||||
{
|
||||
Order[i] = rand.Next();
|
||||
}
|
||||
Array.Sort(Order, Words);
|
||||
}
|
||||
|
||||
private string GetAnAffirmation()
|
||||
{
|
||||
return Affirmations[rand.Next(Affirmations.Length)];
|
||||
}
|
||||
|
||||
private bool CheckTheResponse(string WordName, int WordIndex, string LineInput, string[] WordList)
|
||||
{
|
||||
if (LineInput.Equals("help"))
|
||||
{
|
||||
// Choose a random correct synonym response that doesn't equal the current word given
|
||||
int HelpIndex = rand.Next(WordList.Length);
|
||||
while (HelpIndex == WordIndex)
|
||||
{
|
||||
HelpIndex = rand.Next(0, WordList.Length);
|
||||
}
|
||||
Console.WriteLine("**** A synonym of {0} is {1}.", WordName, WordList[HelpIndex]);
|
||||
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Check to see if the response is one of the listed synonyms and not the current word prompt
|
||||
if (WordList.Contains(LineInput) && LineInput != WordName)
|
||||
{
|
||||
// Randomly display one of the five correct answer exclamations
|
||||
Console.WriteLine(GetAnAffirmation());
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Incorrect response. Try again.
|
||||
Console.WriteLine(" Try again.".PadLeft(5));
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string PromptForSynonym(string WordName)
|
||||
{
|
||||
Console.Write(" What is a synonym of {0}? ", WordName);
|
||||
string LineInput = Console.ReadLine().Trim().ToLower();
|
||||
|
||||
return LineInput;
|
||||
}
|
||||
|
||||
private void AskForSynonyms()
|
||||
{
|
||||
Random rand = new Random();
|
||||
|
||||
// Loop through the now randomized list of Words and display a random word from each to prompt for a synonym
|
||||
foreach (string[] WordList in Words)
|
||||
{
|
||||
int WordIndex = rand.Next(WordList.Length); // random word position in the current list of words
|
||||
string WordName = WordList[WordIndex]; // what is that actual word
|
||||
bool Success = false;
|
||||
|
||||
while (!Success)
|
||||
{
|
||||
// Ask for the synonym of the current word
|
||||
string LineInput = PromptForSynonym(WordName);
|
||||
|
||||
// Check the response
|
||||
Success = CheckTheResponse(WordName, WordIndex, LineInput, WordList);
|
||||
|
||||
// Add extra line space for formatting
|
||||
Console.WriteLine("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void PlayTheGame()
|
||||
{
|
||||
RandomizeTheList();
|
||||
|
||||
DisplayIntro();
|
||||
|
||||
AskForSynonyms();
|
||||
|
||||
DisplayOutro();
|
||||
}
|
||||
}
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
|
||||
new Synonym().PlayTheGame();
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<LangVersion>10</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<StartupObject>Synonym.Program</StartupObject>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
|
||||
@@ -67,16 +67,16 @@ directory for the java or kotlin file, and the class that contains the `main` me
|
||||
The `build.gradle` file **should** be identical to all the other `build.gradle` files
|
||||
in all the other subprojects:
|
||||
```groovy
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs "../../$gameSource"
|
||||
}
|
||||
}
|
||||
}
|
||||
application {
|
||||
mainClass = gameMain
|
||||
}
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs "../../$gameSource"
|
||||
}
|
||||
}
|
||||
}
|
||||
application {
|
||||
mainClass = gameMain
|
||||
}
|
||||
```
|
||||
|
||||
The `gradle.properties` file should look like this:
|
||||
@@ -92,3 +92,84 @@ project to the list.
|
||||
```groovy
|
||||
include ":build_91_Train_java"
|
||||
```
|
||||
|
||||
### Adding a game with tests
|
||||
|
||||
You can add tests for JVM games with a `build.gradle` looking a little different.
|
||||
Use the build files from `03_Animal` as a template to add tests:
|
||||
|
||||
```groovy
|
||||
sourceSets {
|
||||
main {
|
||||
java {
|
||||
srcDirs "../../$gameSource"
|
||||
}
|
||||
}
|
||||
test {
|
||||
java {
|
||||
srcDirs "../../$gameTest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass = gameMain
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation(project(":build_00_utilities").sourceSets.test.output)
|
||||
}
|
||||
```
|
||||
|
||||
The gradle.properties needs an additional directory name for the tests, as `gameTest` :
|
||||
```
|
||||
gameSource=03_Animal/java/src
|
||||
gameTest=03_Animal/java/test
|
||||
gameMain=Animal
|
||||
```
|
||||
|
||||
Each project should have its own test, and shouldn't share test source directories
|
||||
with other projects, even if they are for the same game.
|
||||
|
||||
Tests are constructed by subclassing `ConsoleTest`. This allows you to use the
|
||||
`assertConversation` function to check for correct interactive conversations.
|
||||
```kotlin
|
||||
import com.pcholt.console.testutils.ConsoleTest
|
||||
import org.junit.Test
|
||||
|
||||
class AnimalJavaTest : ConsoleTest() {
|
||||
@Test
|
||||
fun `should have a simple conversation`() {
|
||||
assertConversation(
|
||||
"""
|
||||
WHAT'S YOUR NAME? {PAUL}
|
||||
YOUR NAME IS PAUL? {YES}
|
||||
THANKS FOR PLAYING
|
||||
"""
|
||||
) {
|
||||
// The game's Main method
|
||||
main()
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Curly brackets are the expected user input.
|
||||
Note - this is actually just a way of defining the expected input as "PAUL" and "YES"
|
||||
and not that the input happens at the exact prompt position. Thus this is equivalent:
|
||||
```kotlin
|
||||
"""
|
||||
{PAUL} {YES} WHAT'S YOUR NAME?
|
||||
YOUR NAME IS PAUL?
|
||||
THANKS FOR PLAYING
|
||||
"""
|
||||
```
|
||||
|
||||
Amounts of whitespace are not counted, but whitespace is significant: You will get a failure if
|
||||
your game emits `"NAME?"` when it expects `"NAME ?"`.
|
||||
|
||||
Run all the tests from within the buildJvm project directory:
|
||||
```bash
|
||||
cd buildJvm
|
||||
./gradlew test
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user