mirror of
https://github.com/coding-horror/basic-computer-games.git
synced 2025-12-28 05:33:48 -08:00
Merge branch 'coding-horror:main' into 75_Roulette_Java
This commit is contained in:
84
00_Utilities/find-missing-implementations.js
Normal file
84
00_Utilities/find-missing-implementations.js
Normal file
@@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Program to find games that are missing solutions in a given language
|
||||
*
|
||||
* Scan each game folder, check for a folder for each language, and also make
|
||||
* sure there's at least one file of the expected extension and not just a
|
||||
* readme or something
|
||||
*/
|
||||
|
||||
const fs = require("fs");
|
||||
const glob = require("glob");
|
||||
|
||||
// relative path to the repository root
|
||||
const ROOT_PATH = "../.";
|
||||
|
||||
const languages = [
|
||||
{ name: "csharp", extension: "cs" },
|
||||
{ name: "java", extension: "java" },
|
||||
{ name: "javascript", extension: "html" },
|
||||
{ name: "pascal", extension: "pas" },
|
||||
{ name: "perl", extension: "pl" },
|
||||
{ name: "python", extension: "py" },
|
||||
{ name: "ruby", extension: "rb" },
|
||||
{ name: "vbnet", extension: "vb" },
|
||||
];
|
||||
|
||||
const getFilesRecursive = async (path, extension) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
glob(`${path}/**/*.${extension}`, (err, matches) => {
|
||||
if (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve(matches);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const getPuzzleFolders = () => {
|
||||
return fs
|
||||
.readdirSync(ROOT_PATH, { withFileTypes: true })
|
||||
.filter((dirEntry) => dirEntry.isDirectory())
|
||||
.filter(
|
||||
(dirEntry) =>
|
||||
![".git", "node_modules", "00_Utilities"].includes(dirEntry.name)
|
||||
)
|
||||
.map((dirEntry) => dirEntry.name);
|
||||
};
|
||||
|
||||
(async () => {
|
||||
let missingGames = {};
|
||||
let missingLanguageCounts = {};
|
||||
languages.forEach((l) => (missingLanguageCounts[l.name] = 0));
|
||||
const puzzles = getPuzzleFolders();
|
||||
for (const puzzle of puzzles) {
|
||||
for (const { name: language, extension } of languages) {
|
||||
const files = await getFilesRecursive(
|
||||
`${ROOT_PATH}/${puzzle}/${language}`,
|
||||
extension
|
||||
);
|
||||
if (files.length === 0) {
|
||||
if (!missingGames[puzzle]) missingGames[puzzle] = [];
|
||||
|
||||
missingGames[puzzle].push(language);
|
||||
missingLanguageCounts[language]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
const missingCount = Object.values(missingGames).flat().length;
|
||||
if (missingCount === 0) {
|
||||
console.log("All games have solutions for all languages");
|
||||
} else {
|
||||
console.log(`Missing ${missingCount} implementations:`);
|
||||
|
||||
Object.entries(missingGames).forEach(
|
||||
([p, ls]) => (missingGames[p] = ls.join(", "))
|
||||
);
|
||||
|
||||
console.log(`\nMissing languages by game:`);
|
||||
console.table(missingGames);
|
||||
console.log(`\nBy language:`);
|
||||
console.table(missingLanguageCounts);
|
||||
}
|
||||
})();
|
||||
|
||||
return;
|
||||
1
09_Battle/java/.gitignore
vendored
Normal file
1
09_Battle/java/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*~
|
||||
168
09_Battle/java/Battle.java
Normal file
168
09_Battle/java/Battle.java
Normal file
@@ -0,0 +1,168 @@
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Random;
|
||||
import java.util.function.Predicate;
|
||||
import java.text.NumberFormat;
|
||||
|
||||
|
||||
/* This class holds the game state and the game logic */
|
||||
public class Battle {
|
||||
|
||||
/* parameters of the game */
|
||||
private int seaSize;
|
||||
private int[] sizes;
|
||||
private int[] counts;
|
||||
|
||||
/* The game setup - the ships and the sea */
|
||||
private ArrayList<Ship> ships;
|
||||
private Sea sea;
|
||||
|
||||
/* game state counts */
|
||||
private int[] losses; // how many of each type of ship have been sunk
|
||||
private int hits; // how many hits the player has made
|
||||
private int misses; // how many misses the player has made
|
||||
|
||||
// Names of ships of each size. The game as written has ships of size 3, 4 and 5 but
|
||||
// can easily be modified. It makes no sense to have a ship of size zero though.
|
||||
private static String NAMES_BY_SIZE[] = {
|
||||
"error",
|
||||
"size1",
|
||||
"destroyer",
|
||||
"cruiser",
|
||||
"aircraft carrier",
|
||||
"size5" };
|
||||
|
||||
// Entrypoint
|
||||
public static void main(String args[]) {
|
||||
Battle game = new Battle(6, // Sea is 6 x 6 tiles
|
||||
new int[] { 2, 3, 4 }, // Ships are of sizes 2, 3, and 4
|
||||
new int[] { 2, 2, 2 }); // there are two ships of each size
|
||||
game.play();
|
||||
}
|
||||
|
||||
public Battle(int scale, int[] shipSizes, int[] shipCounts) {
|
||||
seaSize = scale;
|
||||
sizes = shipSizes;
|
||||
counts = shipCounts;
|
||||
|
||||
// validate parameters
|
||||
if (seaSize < 4) throw new RuntimeException("Sea Size " + seaSize + " invalid, must be at least 4");
|
||||
|
||||
for (int sz : sizes) {
|
||||
if ((sz < 1) || (sz > seaSize))
|
||||
throw new RuntimeException("Ship has invalid size " + sz);
|
||||
}
|
||||
|
||||
if (counts.length != sizes.length) {
|
||||
throw new RuntimeException("Ship counts must match");
|
||||
}
|
||||
|
||||
// Initialize game state
|
||||
sea = new Sea(seaSize); // holds what ship if any occupies each tile
|
||||
ships = new ArrayList<Ship>(); // positions and states of all the ships
|
||||
losses = new int[counts.length]; // how many ships of each type have been sunk
|
||||
|
||||
// Build up the list of all the ships
|
||||
int shipNumber = 1;
|
||||
for (int type = 0; type < counts.length; ++type) {
|
||||
for (int i = 0; i < counts[i]; ++i) {
|
||||
ships.add(new Ship(shipNumber++, sizes[type]));
|
||||
}
|
||||
}
|
||||
|
||||
// When we put the ships in the sea, we put the biggest ones in first, or they might
|
||||
// not fit
|
||||
ArrayList<Ship> largestFirst = new ArrayList<>(ships);
|
||||
Collections.sort(largestFirst, Comparator.comparingInt((Ship ship) -> ship.size()).reversed());
|
||||
|
||||
// place each ship into the sea
|
||||
for (Ship ship : largestFirst) {
|
||||
ship.placeRandom(sea);
|
||||
}
|
||||
}
|
||||
|
||||
public void play() {
|
||||
System.out.println("The following code of the bad guys' fleet disposition\nhas been captured but not decoded:\n");
|
||||
System.out.println(sea.encodedDump());
|
||||
System.out.println("De-code it and use it if you can\nbut keep the de-coding method a secret.\n");
|
||||
|
||||
int lost = 0;
|
||||
System.out.println("Start game");
|
||||
Input input = new Input(seaSize);
|
||||
try {
|
||||
while (lost < ships.size()) { // the game continues while some ships remain unsunk
|
||||
if (! input.readCoordinates()) { // ... unless there is no more input from the user
|
||||
return;
|
||||
}
|
||||
|
||||
// The computer thinks of the sea as a grid of rows, from top to bottom.
|
||||
// However, the user will use X and Y coordinates, with Y going bottom to top
|
||||
int row = seaSize - input.y();
|
||||
int col = input.x() - 1;
|
||||
|
||||
if (sea.isEmpty(col, row)) {
|
||||
++misses;
|
||||
System.out.println("Splash! Try again.");
|
||||
} else {
|
||||
Ship ship = ships.get(sea.get(col, row) - 1);
|
||||
if (ship.isSunk()) {
|
||||
++misses;
|
||||
System.out.println("There used to be a ship at that point, but you sunk it.");
|
||||
System.out.println("Splash! Try again.");
|
||||
} else if (ship.wasHit(col, row)) {
|
||||
++misses;
|
||||
System.out.println("You already put a hole in ship number " + ship.id());
|
||||
System.out.println("Splash! Try again.");
|
||||
} else {
|
||||
ship.hit(col, row);
|
||||
++hits;
|
||||
System.out.println("A direct hit on ship number " + ship.id());
|
||||
|
||||
// If a ship was hit, we need to know whether it was sunk.
|
||||
// If so, tell the player and update our counts
|
||||
if (ship.isSunk()) {
|
||||
++lost;
|
||||
System.out.println("And you sunk it. Hurrah for the good guys.");
|
||||
System.out.print("So far, the bad guys have lost ");
|
||||
ArrayList<String> typeDescription = new ArrayList<>();
|
||||
for (int i = 0 ; i < sizes.length; ++i) {
|
||||
if (sizes[i] == ship.size()) {
|
||||
++losses[i];
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(losses[i]);
|
||||
sb.append(" ");
|
||||
sb.append(NAMES_BY_SIZE[sizes[i]]);
|
||||
if (losses[i] != 1)
|
||||
sb.append("s");
|
||||
typeDescription.add(sb.toString());
|
||||
}
|
||||
System.out.println(String.join(", ", typeDescription));
|
||||
double ratioNum = ((double)misses)/hits;
|
||||
String ratio = NumberFormat.getInstance().format(ratioNum);
|
||||
System.out.println("Your current splash/hit ratio is " + ratio);
|
||||
|
||||
if (lost == ships.size()) {
|
||||
System.out.println("You have totally wiped out the bad guys' fleet");
|
||||
System.out.println("With a final splash/hit ratio of " + ratio);
|
||||
|
||||
if (misses == 0) {
|
||||
System.out.println("Congratulations - A direct hit every time.");
|
||||
}
|
||||
|
||||
System.out.println("\n****************************\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
// This should not happen running from console, but java requires us to check for it
|
||||
System.err.println("System error.\n" + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
67
09_Battle/java/Input.java
Normal file
67
09_Battle/java/Input.java
Normal file
@@ -0,0 +1,67 @@
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.text.NumberFormat;
|
||||
|
||||
// This class handles reading input from the player
|
||||
// Each input is an x and y coordinate
|
||||
// e.g. 5,3
|
||||
public class Input {
|
||||
private BufferedReader reader;
|
||||
private NumberFormat parser;
|
||||
private int scale; // size of the sea, needed to validate input
|
||||
private boolean isQuit; // whether the input has ended
|
||||
private int[] coords; // the last coordinates read
|
||||
|
||||
public Input(int seaSize) {
|
||||
scale = seaSize;
|
||||
reader = new BufferedReader(new InputStreamReader(System.in));
|
||||
parser = NumberFormat.getIntegerInstance();
|
||||
}
|
||||
|
||||
public boolean readCoordinates() throws IOException {
|
||||
while (true) {
|
||||
// Write a prompt
|
||||
System.out.print("\nTarget x,y\n> ");
|
||||
String inputLine = reader.readLine();
|
||||
if (inputLine == null) {
|
||||
// If the input stream is ended, there is no way to continue the game
|
||||
System.out.println("\nGame quit\n");
|
||||
isQuit = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// split the input into two fields
|
||||
String[] fields = inputLine.split(",");
|
||||
if (fields.length != 2) {
|
||||
// has to be exactly two
|
||||
System.out.println("Need two coordinates separated by ','");
|
||||
continue;
|
||||
}
|
||||
|
||||
coords = new int[2];
|
||||
boolean error = false;
|
||||
// each field should contain an integer from 1 to the size of the sea
|
||||
try {
|
||||
for (int c = 0 ; c < 2; ++c ) {
|
||||
int val = Integer.parseInt(fields[c].strip());
|
||||
if ((val < 1) || (val > scale)) {
|
||||
System.out.println("Coordinates must be from 1 to " + scale);
|
||||
error = true;
|
||||
} else {
|
||||
coords[c] = val;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (NumberFormatException ne) {
|
||||
// this happens if the field is not a valid number
|
||||
System.out.println("Coordinates must be numbers");
|
||||
error = true;
|
||||
}
|
||||
if (!error) return true;
|
||||
}
|
||||
}
|
||||
|
||||
public int x() { return coords[0]; }
|
||||
public int y() { return coords[1]; }
|
||||
}
|
||||
60
09_Battle/java/Sea.java
Normal file
60
09_Battle/java/Sea.java
Normal file
@@ -0,0 +1,60 @@
|
||||
// Track the content of the sea
|
||||
class Sea {
|
||||
// the sea is a square grid of tiles. It is a one-dimensional array, and this
|
||||
// class maps x and y coordinates to an array index
|
||||
// Each tile is either empty (value of tiles at index is 0)
|
||||
// or contains a ship (value of tiles at index is the ship number)
|
||||
private int tiles[];
|
||||
|
||||
private int size;
|
||||
|
||||
public Sea(int make_size) {
|
||||
size = make_size;
|
||||
tiles = new int[size*size];
|
||||
}
|
||||
|
||||
public int size() { return size; }
|
||||
|
||||
// This writes out a representation of the sea, but in a funny order
|
||||
// The idea is to give the player the job of working it out
|
||||
public String encodedDump() {
|
||||
StringBuilder out = new StringBuilder();
|
||||
for (int x = 0; x < size; ++x) {
|
||||
for (int y = 0; y < size; ++y)
|
||||
out.append(Integer.toString(get(x, y)));
|
||||
out.append('\n');
|
||||
}
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
/* return true if x,y is in the sea and empty
|
||||
* return false if x,y is occupied or is out of range
|
||||
* Doing this in one method makes placing ships much easier
|
||||
*/
|
||||
public boolean isEmpty(int x, int y) {
|
||||
if ((x<0)||(x>=size)||(y<0)||(y>=size)) return false;
|
||||
return (get(x,y) == 0);
|
||||
}
|
||||
|
||||
/* return the ship number, or zero if no ship.
|
||||
* Unlike isEmpty(x,y), these other methods require that the
|
||||
* coordinates passed be valid
|
||||
*/
|
||||
public int get(int x, int y) {
|
||||
return tiles[index(x,y)];
|
||||
}
|
||||
|
||||
public void set(int x, int y, int value) {
|
||||
tiles[index(x, y)] = value;
|
||||
}
|
||||
|
||||
// map the coordinates to the array index
|
||||
private int index(int x, int y) {
|
||||
if ((x < 0) || (x >= size))
|
||||
throw new ArrayIndexOutOfBoundsException("Program error: x cannot be " + x);
|
||||
if ((y < 0) || (y >= size))
|
||||
throw new ArrayIndexOutOfBoundsException("Program error: y cannot be " + y);
|
||||
|
||||
return y*size + x;
|
||||
}
|
||||
}
|
||||
170
09_Battle/java/Ship.java
Normal file
170
09_Battle/java/Ship.java
Normal file
@@ -0,0 +1,170 @@
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Random;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/** A single ship, with its position and where it has been hit */
|
||||
class Ship {
|
||||
// These are the four directions that ships can be in
|
||||
public static final int ORIENT_E=0; // goes East from starting position
|
||||
public static final int ORIENT_SE=1; // goes SouthEast from starting position
|
||||
public static final int ORIENT_S=2; // goes South from starting position
|
||||
public static final int ORIENT_SW=3; // goes SouthWest from starting position
|
||||
|
||||
private int id; // ship number
|
||||
private int size; // how many tiles it occupies
|
||||
private boolean placed; // whether this ship is in the sea yet
|
||||
private boolean sunk; // whether this ship has been sunk
|
||||
private ArrayList<Boolean> hits; // which tiles of the ship have been hit
|
||||
|
||||
private int startX; // starting position coordinates
|
||||
private int startY;
|
||||
private int orientX; // x and y deltas from each tile occupied to the next
|
||||
private int orientY;
|
||||
|
||||
public Ship(int i, int sz) {
|
||||
id = i; size = sz;
|
||||
sunk = false; placed = false;
|
||||
hits = new ArrayList<>(Collections.nCopies(size, false));
|
||||
}
|
||||
|
||||
/** @returns the ship number */
|
||||
public int id() { return id; }
|
||||
/** @returns the ship size */
|
||||
public int size() { return size; }
|
||||
|
||||
/* record the ship as having been hit at the given coordinates */
|
||||
public void hit(int x, int y) {
|
||||
// need to work out how many tiles from the ship's starting position the hit is at
|
||||
// that can be worked out from the difference between the starting X coord and this one
|
||||
// unless the ship runs N-S, in which case use the Y coord instead
|
||||
int offset;
|
||||
if (orientX != 0) {
|
||||
offset = (x - startX) / orientX;
|
||||
} else {
|
||||
offset = (y - startY) / orientY;
|
||||
}
|
||||
hits.set(offset, true);
|
||||
|
||||
// if every tile of the ship has been hit, the ship is sunk
|
||||
sunk = hits.stream().allMatch(Predicate.isEqual(true));
|
||||
}
|
||||
|
||||
public boolean isSunk() { return sunk; }
|
||||
|
||||
// whether the ship has already been hit at the given coordinates
|
||||
public boolean wasHit(int x, int y) {
|
||||
int offset;
|
||||
if (orientX != 0) {
|
||||
offset = (x - startX) / orientX;
|
||||
} else {
|
||||
offset = (y - startY) / orientY;
|
||||
}
|
||||
return hits.get(offset);
|
||||
};
|
||||
|
||||
// Place the ship in the sea.
|
||||
// choose a random starting position, and a random direction
|
||||
// if that doesn't fit, keep picking different positions and directions
|
||||
public void placeRandom(Sea s) {
|
||||
Random random = new Random();
|
||||
for (int tries = 0 ; tries < 1000 ; ++tries) {
|
||||
int x = random.nextInt(s.size());
|
||||
int y = random.nextInt(s.size());
|
||||
int orient = random.nextInt(4);
|
||||
|
||||
if (place(s, x, y, orient)) return;
|
||||
}
|
||||
|
||||
throw new RuntimeException("Could not place any more ships");
|
||||
}
|
||||
|
||||
// Attempt to fit the ship into the sea, starting from a given position and
|
||||
// in a given direction
|
||||
// This is by far the most complicated part of the program.
|
||||
// It will start at the position provided, and attempt to occupy tiles in the
|
||||
// requested direction. If it does not fit, either because of the edge of the
|
||||
// sea, or because of ships already in place, it will try to extend the ship
|
||||
// in the opposite direction instead. If that is not possible, it fails.
|
||||
public boolean place(Sea s, int x, int y, int orient) {
|
||||
if (placed) {
|
||||
throw new RuntimeException("Program error - placed ship " + id + " twice");
|
||||
}
|
||||
switch(orient) {
|
||||
case ORIENT_E: // east is increasing X coordinate
|
||||
orientX = 1; orientY = 0;
|
||||
break;
|
||||
case ORIENT_SE: // southeast is increasing X and Y
|
||||
orientX = 1; orientY = 1;
|
||||
break;
|
||||
case ORIENT_S: // south is increasing Y
|
||||
orientX = 0; orientY = 1;
|
||||
break;
|
||||
case ORIENT_SW: // southwest is increasing Y but decreasing X
|
||||
orientX = -1; orientY = 1;
|
||||
break;
|
||||
default:
|
||||
throw new RuntimeException("Invalid orientation " + orient);
|
||||
}
|
||||
|
||||
if (!s.isEmpty(x, y)) return false; // starting position is occupied - placing fails
|
||||
|
||||
startX = x; startY = y;
|
||||
int tilesPlaced = 1;
|
||||
int nextX = startX;
|
||||
int nextY = startY;
|
||||
while (tilesPlaced < size) {
|
||||
if (extendShip(s, nextX, nextY, nextX + orientX, nextY + orientY)) {
|
||||
// It is clear to extend the ship forwards
|
||||
tilesPlaced += 1;
|
||||
nextX = nextX + orientX;
|
||||
nextY = nextY + orientY;
|
||||
} else {
|
||||
int backX = startX - orientX;
|
||||
int backY = startY - orientY;
|
||||
|
||||
if (extendShip(s, startX, startY, backX, backY)) {
|
||||
// We can move the ship backwards, so it can be one tile longer
|
||||
tilesPlaced +=1;
|
||||
startX = backX;
|
||||
startY = backY;
|
||||
} else {
|
||||
// Could not make it longer or move it backwards
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mark in the sea which tiles this ship occupies
|
||||
for (int i = 0; i < size; ++i) {
|
||||
int sx = startX + i * orientX;
|
||||
int sy = startY + i * orientY;
|
||||
s.set(sx, sy, id);
|
||||
}
|
||||
|
||||
placed = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check whether a ship which already occupies the "from" coordinates,
|
||||
// can also occupy the "to" coordinates.
|
||||
// They must be within the sea area, empty, and not cause the ship to cross
|
||||
// over another ship
|
||||
private boolean extendShip(Sea s, int fromX, int fromY, int toX, int toY) {
|
||||
if (!s.isEmpty(toX, toY)) return false; // no space
|
||||
if ((fromX == toX)||(fromY == toY)) return true; // horizontal or vertical
|
||||
|
||||
// we can extend the ship without colliding, but we are going diagonally
|
||||
// and it should not be possible for two ships to cross each other on
|
||||
// opposite diagonals.
|
||||
|
||||
// check the two tiles that would cross us here - if either is empty, we are OK
|
||||
// if they both contain different ships, we are OK
|
||||
// but if they both contain the same ship, we are crossing!
|
||||
int corner1 = s.get(fromX, toY);
|
||||
int corner2 = s.get(toX, fromY);
|
||||
if ((corner1 == 0) || (corner1 != corner2)) return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -23,16 +23,19 @@ Alternatively, for non-dotnet compatible translations, you will need [Visual Stu
|
||||
|
||||
## java
|
||||
|
||||
**TIP:** You can build all the java and kotlin games at once
|
||||
using the instructions in the [buildJvm directory](buildJvm/README.md)
|
||||
|
||||
The Java translations can be run via the command line or from an IDE such as [Eclipse](https://www.eclipse.org/downloads/packages/release/kepler/sr1/eclipse-ide-java-developers) or [IntelliJ](https://www.jetbrains.com/idea/)
|
||||
|
||||
To run from the command line, you will need a Java SDK (eg. [Oracle JDK](https://www.oracle.com/java/technologies/downloads/) or [Open JDK](https://openjdk.java.net/)).
|
||||
|
||||
1. Navigate to the corresponding directory.
|
||||
1. Compile the program with `javac`:
|
||||
* eg. `javac AceyDuceyGame.java`
|
||||
* eg. `javac AceyDuceyGame.java`
|
||||
1. Run the compiled program with `java`:
|
||||
* eg. `java AceyDuceyGame`
|
||||
|
||||
* eg. `java AceyDuceyGame`
|
||||
|
||||
or if you are **using JDK11 or later** you can now execute a self contained java file that has a main method directly with `java <filename>.java`.
|
||||
|
||||
## javascript
|
||||
@@ -41,6 +44,11 @@ The javascript examples can be run from within your web browser:
|
||||
|
||||
1. Simply open the corresponding `.html` file from your web browser.
|
||||
|
||||
## kotlin
|
||||
|
||||
Use the directions in [buildJvm](buildJvm/README.md) to build for kotlin. You can also use those directions to
|
||||
build java games.
|
||||
|
||||
## pascal
|
||||
|
||||
The pascal examples can be run using [Free Pascal](https://www.freepascal.org/). Additionally, `.lsi` project files can be opened with the [Lazarus Project IDE](https://www.lazarus-ide.org/).
|
||||
@@ -48,7 +56,7 @@ The pascal examples can be run using [Free Pascal](https://www.freepascal.org/).
|
||||
The pascal examples include both *simple* (single-file) and *object-oriented* (in the `/object-pascal`directories) examples.
|
||||
|
||||
1. You can compile the program from the command line with the `fpc` command.
|
||||
* eg. `fpc amazing.pas`
|
||||
* eg. `fpc amazing.pas`
|
||||
1. The output is an executable file that can be run directly.
|
||||
|
||||
## perl
|
||||
@@ -57,7 +65,7 @@ The perl translations can be run using a perl interpreter (a copy can be downloa
|
||||
|
||||
1. From the command-line, navigate to the corresponding directory.
|
||||
1. Invoke with the `perl` command.
|
||||
* eg. `perl aceyducey.pl`
|
||||
* eg. `perl aceyducey.pl`
|
||||
|
||||
## python
|
||||
|
||||
@@ -65,8 +73,8 @@ The python translations can be run from the command line by using the `py` inter
|
||||
|
||||
1. From the command-line, navigate to the corresponding directory.
|
||||
1. Invoke with the `py` or `python` interpreter (depending on your python version).
|
||||
* eg. `py acey_ducey_oo.py`
|
||||
* eg. `python aceyducey.py`
|
||||
* eg. `py acey_ducey_oo.py`
|
||||
* eg. `python aceyducey.py`
|
||||
|
||||
**Note**
|
||||
|
||||
@@ -80,7 +88,7 @@ If you don't already have a ruby interpreter, you can download it from the [ruby
|
||||
|
||||
1. From the command-line, navigate to the corresponding directory.
|
||||
1. Invoke with the `ruby` tool.
|
||||
* eg. `ruby aceyducey.rb`
|
||||
* eg. `ruby aceyducey.rb`
|
||||
|
||||
## vbnet
|
||||
|
||||
|
||||
@@ -1,25 +1,56 @@
|
||||
# JVM gradle scripts
|
||||
|
||||
## Quickstart
|
||||
You will need to install openjdk 17, because some games use advanced Java features.
|
||||
We should be using version 17 anyway, because anything less than 17 is deprecated.
|
||||
|
||||
Build all the games:
|
||||
|
||||
cd buildJvm
|
||||
./gradlew -q assemble installDist distributeBin distributeLib
|
||||
```shell
|
||||
cd buildJvm
|
||||
./gradlew -q assemble installDist distributeBin distributeLib
|
||||
```
|
||||
|
||||
Then, run a game
|
||||
|
||||
### Mac or linux:
|
||||
|
||||
build/distrib/bin/build_53_King_kotlin
|
||||
|
||||
```shell
|
||||
build/distrib/bin/build_53_King_kotlin
|
||||
```
|
||||
### Windows
|
||||
[not tested yet]
|
||||
|
||||
build\distrib\bin\build_53_King_kotlin.bat
|
||||
```shell
|
||||
build\distrib\bin\build_53_King_kotlin.bat
|
||||
```
|
||||
|
||||
---
|
||||
## Using an IDE to work on JVM games
|
||||
|
||||
You can open the entire Basic Computer Games project in an IDE, with any IDE capable
|
||||
of importing from a gradle project.
|
||||
|
||||
### IntelliJ / Android Studio
|
||||
|
||||
1. (Optional) If you want to make changes, or contribute a new kotlin or java version
|
||||
of one of the games, use [github "fork"](https://docs.github.com/en/get-started/quickstart/fork-a-repo)
|
||||
to create your own editable fork of the project.
|
||||
2. Check out the code using `File` -> `New` -> `Project from Version Control`
|
||||
1. Enter the URL of the project. For the main project this will be `https://github.com/coding-horror/basic-computer-games.git`, for your
|
||||
own fork this will be `https://github.com/YOURNAMEHERE/basic-computer-games.git`
|
||||
2. Choose a directory for the clone to live in
|
||||
3. Click `Clone`
|
||||
|
||||
The project will open, and eventually you will get a little alert box in the bottom right corner saying "Gradle build script found".
|
||||
|
||||
Click the "Load" link in the alert box, to load the gradle project.
|
||||
|
||||
You should see all the games appear on the left side of the screen. If you have loaded
|
||||
your own fork, you can modify, commit and push your changes to github.
|
||||
|
||||
If you are using the main `coding-horror` branch, you can still make and run your own changes. If
|
||||
your git skills are up to the task, you might even fork the project and change your
|
||||
local clone to point to your new forked project.
|
||||
|
||||
You will need to install openjdk 17, because some games use advanced Java features.
|
||||
We should be using version 17 anyway, because anything less than 17 is deprecated.
|
||||
|
||||
---
|
||||
## Adding a new game
|
||||
@@ -34,9 +65,10 @@ there is some special requirement.
|
||||
directory for the java or kotlin file, and the class that contains the `main` method.
|
||||
|
||||
The `build.gradle` file will normally be identical to this:
|
||||
|
||||
```groovy
|
||||
plugins {
|
||||
id 'application'
|
||||
// id 'org.jetbrains.kotlin.jvm' // UNCOMMENT for kotlin projects
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
@@ -54,6 +86,7 @@ The `build.gradle` file will normally be identical to this:
|
||||
application {
|
||||
mainClass = gameMain
|
||||
}
|
||||
```
|
||||
|
||||
And the `gradle.properties` file should look like this:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user