init
This commit is contained in:
BIN
Enigma/Enigma.class
Normal file
BIN
Enigma/Enigma.class
Normal file
Binary file not shown.
67
Enigma/Enigma.java
Normal file
67
Enigma/Enigma.java
Normal file
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Enigma Class with method for en-/decoding of characters.
|
||||
* Constructs the Enigma.
|
||||
*/
|
||||
public class Enigma {
|
||||
|
||||
/**
|
||||
* Patchboard of the Enigma.
|
||||
*/
|
||||
private PatchBoard patchboard;
|
||||
|
||||
/**
|
||||
* Reflector of the Engima.
|
||||
*/
|
||||
private Reflector reflector;
|
||||
|
||||
/**
|
||||
* Array of variable mount of Enigma's rotors.
|
||||
*/
|
||||
private Rotor[] rotor;
|
||||
|
||||
/**
|
||||
* Creates the Enigma with patchboard, reflector and all the rotors.
|
||||
* Arguments are committed by the Shell.
|
||||
* @param tickPos tick-position-array for all rotors
|
||||
* @param initialPos initial-position-array
|
||||
* @param permu permutation-array-array
|
||||
* @param patchboardpermu Permutation for the patchboard
|
||||
* @param reflectorpermu Permutation for the reflector
|
||||
*/
|
||||
Enigma(final int[] tickPos, final int[] initialPos, final int[][] permu
|
||||
, final int[] patchboardpermu, final int[] reflectorpermu) {
|
||||
this.patchboard = new PatchBoard(patchboardpermu);
|
||||
this.reflector = new Reflector(reflectorpermu);
|
||||
rotor = new Rotor[tickPos.length];
|
||||
for (int i = 0; i < tickPos.length; i++) {
|
||||
rotor[i] = new Rotor(tickPos[i],
|
||||
initialPos[i], permu[i]);
|
||||
if (0 < i) {
|
||||
this.rotor[i - 1].setNext(rotor[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for en-/decoding of a Character. Calls one by one the patchboard
|
||||
* , all the rotors, the Reflector, back again all the rotors and finally
|
||||
* again the patchboard like in the Enigma, used by the Germans in WW2.
|
||||
* @param symbol int-value of a Character
|
||||
* @return en-/decoded value of a Character
|
||||
*/
|
||||
public final int encode(final int symbol) {
|
||||
int i = symbol;
|
||||
i = patchboard.encode(i);
|
||||
for (int j = 0; j < rotor.length; j++) {
|
||||
i = rotor[j].encode(i);
|
||||
}
|
||||
i = reflector.encode(i);
|
||||
for (int k = rotor.length - 1; k >= 0; k--) {
|
||||
i = this.rotor[k].decode(i);
|
||||
}
|
||||
i = patchboard.decode(i);
|
||||
rotor[0].tick();
|
||||
return i;
|
||||
}
|
||||
|
||||
}
|
||||
BIN
Enigma/EnigmaComponent.class
Normal file
BIN
Enigma/EnigmaComponent.class
Normal file
Binary file not shown.
33
Enigma/EnigmaComponent.java
Normal file
33
Enigma/EnigmaComponent.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Interface EnigmaComponent unifies rotors, patchboard and reflector
|
||||
* of an enigma.
|
||||
*/
|
||||
interface EnigmaComponent {
|
||||
|
||||
/**
|
||||
* Applies the substitution chiffre of this enigma component in the current
|
||||
* state to symbol.
|
||||
*
|
||||
* @param symbol The symbol to encode with the substitution chiffre of this
|
||||
* enigma component in its current state.
|
||||
* @return The symbol which is the encoded symbol of the parameter symbol.
|
||||
*/
|
||||
int encode(int symbol);
|
||||
|
||||
/**
|
||||
* Applies the inverse substitution chiffre of this enigma component in the
|
||||
* current state to letter.
|
||||
*
|
||||
* @param symbol The symbol to decode with the (inverse) substitution
|
||||
* chiffre of this enigma component in its current state.
|
||||
* @return The symbol which is the decoded symbol of the parameter symbol.
|
||||
*/
|
||||
int decode(int symbol);
|
||||
|
||||
/**
|
||||
* In case this component can do ticks, this component performs its next
|
||||
* tick. In case this triggers ticks of subsequent components, their ticks
|
||||
* are also triggered.
|
||||
*/
|
||||
void tick();
|
||||
}
|
||||
BIN
Enigma/PatchBoard.class
Normal file
BIN
Enigma/PatchBoard.class
Normal file
Binary file not shown.
59
Enigma/PatchBoard.java
Normal file
59
Enigma/PatchBoard.java
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* Patchboard for a Enigma.
|
||||
*/
|
||||
public class PatchBoard implements EnigmaComponent {
|
||||
/**
|
||||
* Permutation for the patchboard.
|
||||
*/
|
||||
private int[] permu;
|
||||
|
||||
/**
|
||||
* Inverse permutation for the patchboard.
|
||||
*/
|
||||
private int[] invPermu;
|
||||
|
||||
/**
|
||||
* Constructor for the patchboard.
|
||||
* @param permu permutation
|
||||
*/
|
||||
PatchBoard(final int[] permu) {
|
||||
this.permu = permu;
|
||||
this.invPermu = calcInvPermu(permu);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final int encode(final int i) {
|
||||
return permu[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final int decode(final int i) {
|
||||
return invPermu[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final void tick() {
|
||||
}
|
||||
/**
|
||||
* Calculates the inverese permutation.
|
||||
* @param permu permutation
|
||||
* @return <code>inverse permutation</code>
|
||||
*/
|
||||
private int[] calcInvPermu(final int[] permu) {
|
||||
int[] array = new int[permu.length];
|
||||
for (int i = 0; i < permu.length; i++) {
|
||||
for (int k = 0; k < permu.length; k++) {
|
||||
if (i == permu[k]) {
|
||||
array[i] = k;
|
||||
}
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
||||
BIN
Enigma/Reflector.class
Normal file
BIN
Enigma/Reflector.class
Normal file
Binary file not shown.
40
Enigma/Reflector.java
Normal file
40
Enigma/Reflector.java
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* Reflector for the Enigma.
|
||||
*
|
||||
*/
|
||||
public class Reflector implements EnigmaComponent {
|
||||
|
||||
/**
|
||||
* Permutation of the reflector.
|
||||
*/
|
||||
private int[] permu;
|
||||
/**
|
||||
* Permutation of reflector.
|
||||
* @param permu Permutation
|
||||
*/
|
||||
Reflector(final int[] permu) {
|
||||
this.permu = permu;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final int encode(final int i) {
|
||||
return permu[i];
|
||||
}
|
||||
|
||||
/**
|
||||
* Method is not required in the reflector.<br />
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final int decode(final int i) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public void tick() {
|
||||
|
||||
}
|
||||
}
|
||||
BIN
Enigma/Rotor.class
Normal file
BIN
Enigma/Rotor.class
Normal file
Binary file not shown.
109
Enigma/Rotor.java
Normal file
109
Enigma/Rotor.java
Normal file
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* Class for the rotors of the Enigma.
|
||||
*
|
||||
*/
|
||||
public class Rotor implements EnigmaComponent {
|
||||
/**
|
||||
* Position where rotor "ticks" next rotor.
|
||||
*/
|
||||
private int tickPos;
|
||||
|
||||
/**
|
||||
* Current position of the rotor.
|
||||
*/
|
||||
private int initialPos;
|
||||
|
||||
/**
|
||||
* If Rotor has succesor.
|
||||
*/
|
||||
private Rotor next;
|
||||
|
||||
/**
|
||||
* Permutation of the rotor.
|
||||
*/
|
||||
private int[] permu;
|
||||
|
||||
/**
|
||||
* Inverse permutation of the rotor.
|
||||
*/
|
||||
private int[] invPermu;
|
||||
|
||||
/**
|
||||
* @param tickPos Position where rotor "ticks" next rotor
|
||||
* @param initialPos Current position of the rotor
|
||||
* @param permu Permutation of the rotor
|
||||
*/
|
||||
Rotor(final int tickPos, final int initialPos, final int[] permu) {
|
||||
this.tickPos = tickPos;
|
||||
this.initialPos = initialPos;
|
||||
this.permu = permu;
|
||||
this.invPermu = calcInvPermu(permu);
|
||||
this.next = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the next-rotor-pointer.
|
||||
* @param next Next rotor
|
||||
*/
|
||||
public final void setNext(final Rotor next) {
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the inverese permutation.
|
||||
* @param permu permutation
|
||||
* @return <code>inverse permutation</code>
|
||||
*/
|
||||
private int[] calcInvPermu(final int[] permu) {
|
||||
int[] array = new int[permu.length];
|
||||
for (int i = 0; i < permu.length; i++) {
|
||||
for (int k = 0; k < permu.length; k++) {
|
||||
if (i == permu[k]) {
|
||||
array[i] = k;
|
||||
}
|
||||
}
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final int encode(final int i) {
|
||||
if (((permu[((initialPos + i) % permu.length)] - initialPos
|
||||
+ permu.length) % permu.length) < 0) {
|
||||
return (((permu[((initialPos + i) % permu.length)]
|
||||
- initialPos + permu.length)
|
||||
% permu.length) + permu.length);
|
||||
}
|
||||
return (permu[((initialPos + i) % permu.length)]
|
||||
- initialPos + permu.length) % permu.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final int decode(final int i) {
|
||||
if (((invPermu[((initialPos + i) % permu.length)]
|
||||
- initialPos + permu.length) % permu.length) < 0) {
|
||||
return (((invPermu[((initialPos + i) % permu.length)]
|
||||
- initialPos + permu.length)
|
||||
% permu.length) + permu.length);
|
||||
}
|
||||
return (invPermu[((initialPos + i) % permu.length)]
|
||||
- initialPos + permu.length) % permu.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final void tick() {
|
||||
if (initialPos == permu.length) {
|
||||
initialPos = 0;
|
||||
}
|
||||
initialPos++;
|
||||
if (initialPos == (tickPos + 1) && next != null) {
|
||||
next.tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
Enigma/Shell.class
Normal file
BIN
Enigma/Shell.class
Normal file
Binary file not shown.
252
Enigma/Shell.java
Normal file
252
Enigma/Shell.java
Normal file
@@ -0,0 +1,252 @@
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Main Class for interaction with the user.
|
||||
*
|
||||
*/
|
||||
public final class Shell {
|
||||
/**
|
||||
* number of arguments / rotor.
|
||||
*/
|
||||
private static final int ROTORARGS = 3;
|
||||
/**
|
||||
* Patchboard-permutation.
|
||||
*/
|
||||
private static final int[] PATCHBOARDPERMU = new int[] {9, 22, 20, 11, 2, 12
|
||||
, 13, 14, 7, 15, 16, 25, 24, 23, 8, 17, 0, 3, 10, 4, 6, 21, 1, 19
|
||||
, 18, 5};
|
||||
|
||||
/**
|
||||
* Reflector- permutation.
|
||||
*/
|
||||
private static final int[] REFLECTORPERMU = new int[] {8, 12, 4, 19, 2, 6, 5
|
||||
, 17, 0, 24, 18, 16, 1, 25, 23, 22, 11, 7, 10, 3, 21, 20, 15, 14
|
||||
, 9, 13};
|
||||
|
||||
/**
|
||||
* Amount of Symbols the alphabet uses.
|
||||
*/
|
||||
private static int symbols = PATCHBOARDPERMU.length;
|
||||
|
||||
private Shell() { }
|
||||
|
||||
/**
|
||||
* Convert char-Array to Enigma-permutation-array.
|
||||
* @param charArray char-array to convert
|
||||
* @return Enigma-compatible <code>permutation array</code>
|
||||
*/
|
||||
private static int[] convertCharInt(final char[] charArray) {
|
||||
int[] intArray = new int[charArray.length];
|
||||
for (int i = 0; i < charArray.length; i++) {
|
||||
intArray[i] = (charArray[i] - 65);
|
||||
}
|
||||
return intArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert Enigma-int to char.
|
||||
* @param i Enigma-int
|
||||
* @return <code>char</code>
|
||||
*/
|
||||
private static char convertIntChar(final int i) {
|
||||
return (char) (i + 65);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if every Parameter for a Enigma exists.
|
||||
* @param stringArray Array of all programm parameters
|
||||
* @return <code>true</code> if right number of parameters are given<br />
|
||||
* <code>false</code> if wrong number of parameters or parameters
|
||||
* are zero
|
||||
*/
|
||||
private static boolean checkArgsLength(final String[] stringArray) {
|
||||
return stringArray.length % ROTORARGS == 0 && stringArray.length != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the Permutation-Array Elements out of the Arguments
|
||||
* given by the User. <br /> Every third Element is a Permutation-Array.
|
||||
* Furthermore, the lengths of the Array is checked to handle faults.
|
||||
* @param stringAr the <code>String</code> entered by the user.
|
||||
* @return <code>Permutation-Array[][]</code> for every Rotor as a
|
||||
* char-array or<br /><code>null</code> if any error occurs.
|
||||
*/
|
||||
private static int[][] extractPermuArray(final String[] stringAr) {
|
||||
int[][] intArray = new int[(stringAr.length / ROTORARGS)]
|
||||
[Shell.symbols];
|
||||
for (int i = 0; i < (stringAr.length) / ROTORARGS; i++) {
|
||||
if (stringAr[(i * ROTORARGS)].length() == Shell.symbols) {
|
||||
intArray[i] = convertCharInt(
|
||||
stringAr[(i * ROTORARGS)].toCharArray());
|
||||
} else { return null; }
|
||||
}
|
||||
return intArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the tick-Positions out of string-array into a int-array.
|
||||
* @param stringAr All Arguments committed to the Programm
|
||||
* @return <code>int-array</code> with rotors tick-positions <br />
|
||||
* or <code>null</code> if tick-positition has more then
|
||||
* one character
|
||||
*/
|
||||
private static int[] extractTickPosArray(final String[] stringAr) {
|
||||
int[] intArray = new int[(stringAr.length / ROTORARGS)];
|
||||
for (int i = 0; i < (stringAr.length / ROTORARGS); i++) {
|
||||
if (stringAr[1 + (i * ROTORARGS)].toCharArray().length == 1) {
|
||||
intArray[i] = stringAr[1 + (i * ROTORARGS)].charAt(0) - 65;
|
||||
} else { return null; }
|
||||
}
|
||||
return intArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract the initial-Positions out of string-array into a int-array.
|
||||
* @param stringAr All Arguments committed to the Programm
|
||||
* @return <code>int-array</code> with rotors initial-positions <br />
|
||||
* or <code>null</code> if initial-positition has more then
|
||||
* one character
|
||||
*/
|
||||
private static int[] extractInitialPosArray(final String[] stringAr) {
|
||||
int[] intArray = new int[(stringAr.length / ROTORARGS)];
|
||||
for (int i = 0; i < (stringAr.length / ROTORARGS); i++) {
|
||||
if (stringAr[2 + (i * ROTORARGS)].toCharArray().length == 1) {
|
||||
intArray[i] = stringAr[2 + (i * ROTORARGS)].charAt(0) - 65;
|
||||
} else { return null; }
|
||||
}
|
||||
return intArray;
|
||||
}
|
||||
/**
|
||||
* Checks if char is a capital-Letter.
|
||||
* @param ch Character
|
||||
* @return <code>true</code> <br >/
|
||||
* or <code>false</code>
|
||||
*/
|
||||
private static boolean isEnigmaChar(final int ch) {
|
||||
if (ch < 0 || ch >= Shell.symbols) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if char-array is a capital-Letter-array.
|
||||
* @param ch Character-array
|
||||
* @return <code>true</code> <br >/
|
||||
* or <code>false</code>
|
||||
*/
|
||||
private static boolean isEnigmaCharArray(final int[] ch) {
|
||||
for (int i = 0; i < ch.length; i++) {
|
||||
if (!isEnigmaChar(ch[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if char-array-array is a capital-Letter-array-array.
|
||||
* @param ch Character-array-array
|
||||
* @return <code>true</code> <br >/
|
||||
* or <code>false</code>
|
||||
*/
|
||||
private static boolean isEnigmaCharArray(final int[][] ch) {
|
||||
for (int i = 0; i < ch.length; i++) {
|
||||
if (!isEnigmaCharArray(ch[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/**
|
||||
* Checks if a permutation contains a char twice.
|
||||
* @param permu Permutation
|
||||
* @return <code>true</code> if duplicate char is found<br />
|
||||
* <code>false</code> if chars are unique
|
||||
*/
|
||||
private static boolean hasDuplicateChar(final int[][] permu) {
|
||||
boolean[][] foo = new boolean[permu.length][permu[0].length];
|
||||
int i;
|
||||
int j;
|
||||
for (i = 0; i < permu.length; i++) {
|
||||
for (j = 0; j < permu[i].length; j++) {
|
||||
if (foo[i][permu[i][j]]) {
|
||||
return true;
|
||||
} else {
|
||||
foo[i][permu[i][j]] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main method for the Enigma-Simulation.
|
||||
* @param args All parameter given to the Shell
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
public static void main(final String[] args) throws IOException {
|
||||
int[][] permu = new int[(args.length / ROTORARGS)][Shell.symbols];
|
||||
int[] tickPos = new int[(args.length / ROTORARGS)];
|
||||
int[] initialPos = new int[(args.length / ROTORARGS)];
|
||||
Enigma myEnigma;
|
||||
|
||||
if (checkArgsLength(args)) {
|
||||
if (extractPermuArray(args) != null) {
|
||||
permu = extractPermuArray(args);
|
||||
} else {
|
||||
System.out.println("Error! Wrong Permutation!");
|
||||
return;
|
||||
}
|
||||
if (extractTickPosArray(args) != null) {
|
||||
tickPos = extractTickPosArray(args);
|
||||
} else {
|
||||
System.out.println("Error! wrong Tick Position!");
|
||||
return;
|
||||
}
|
||||
if (extractInitialPosArray(args) != null) {
|
||||
initialPos = extractInitialPosArray(args);
|
||||
} else {
|
||||
System.out.println("Error! wrong initial Position!");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
System.out.println("Error: wrong number of Arguments");
|
||||
return;
|
||||
}
|
||||
//Check if only Capital Letters are used
|
||||
if (!isEnigmaCharArray(permu) || !isEnigmaCharArray(tickPos)
|
||||
|| !isEnigmaCharArray(initialPos)) {
|
||||
System.out.print("Error! wrong character in config");
|
||||
return;
|
||||
}
|
||||
if (hasDuplicateChar(extractPermuArray(args))) {
|
||||
System.out.println("Error! Duplicate char in Permutation");
|
||||
}
|
||||
|
||||
BufferedReader br =
|
||||
new BufferedReader(new InputStreamReader(System.in));
|
||||
|
||||
myEnigma = new Enigma(tickPos, initialPos, permu, PATCHBOARDPERMU
|
||||
, REFLECTORPERMU);
|
||||
|
||||
|
||||
while (true) {
|
||||
String stdinp = br.readLine();
|
||||
if (stdinp.equals("")) {
|
||||
return;
|
||||
}
|
||||
int[] ciphertxt = convertCharInt(stdinp.toCharArray());
|
||||
if (!isEnigmaCharArray(ciphertxt)) {
|
||||
System.out.print("Error! Wrong input");
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < ciphertxt.length; i++) {
|
||||
System.out.print(convertIntChar(myEnigma.encode(ciphertxt[i])));
|
||||
}
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
121
Reversi/Board.java
Normal file
121
Reversi/Board.java
Normal file
@@ -0,0 +1,121 @@
|
||||
package reversi.model;
|
||||
|
||||
|
||||
/**
|
||||
* Interface for a Reversi game, also known as Othello.
|
||||
*
|
||||
* A human plays against the machine.
|
||||
*/
|
||||
public interface Board extends Cloneable {
|
||||
|
||||
/**
|
||||
* The number of rows and columns of the game grid. Originally 8.
|
||||
* Here, even and at least 4.
|
||||
*/
|
||||
int SIZE = 8;
|
||||
|
||||
/**
|
||||
* Get the player who should start or already has started the game.
|
||||
*
|
||||
* @return The player who makes the initial move.
|
||||
*/
|
||||
Player getFirstPlayer();
|
||||
|
||||
/**
|
||||
* Get the player who owns the next game turn.
|
||||
*
|
||||
* @return The player who is allowed to make the next turn.
|
||||
*/
|
||||
Player next();
|
||||
|
||||
/**
|
||||
* Execute a human move.
|
||||
*
|
||||
* @param row The slot's row number where a tile of the human player should
|
||||
* be placed on.
|
||||
* @param col The slot's column number where a tile of the human player
|
||||
* should be placed on.
|
||||
* @return {@code true} if and only if move was successful, e.g., the
|
||||
* defined slot was not occupied and at least one tile of the
|
||||
* machine was reversed.
|
||||
* @throws IllegalMoveException If the game is already over, or it is not
|
||||
* the human's turn.
|
||||
* @throws IllegalArgumentException If the provided parameters are
|
||||
* invalid, e.g., the defined slot was is not on the grid.
|
||||
*/
|
||||
boolean move(int row, int col);
|
||||
|
||||
/**
|
||||
* Execute a machine move.
|
||||
*
|
||||
* @throws IllegalMoveException If the game is already over, or it is not
|
||||
* the machine's turn.
|
||||
*/
|
||||
void machineMove();
|
||||
|
||||
/**
|
||||
* Set the skill level of the machine.
|
||||
*
|
||||
* @param level The skill as a number, must be at least 1.
|
||||
*/
|
||||
void setLevel(int level);
|
||||
|
||||
/**
|
||||
* Check if the game is over. Either one player has won or there is a tie,
|
||||
* i.e., no player can perform a move any more.
|
||||
*
|
||||
* @return {@code true} if and only if the game is over.
|
||||
*/
|
||||
boolean gameOver();
|
||||
|
||||
/**
|
||||
* Check if the game state is won.
|
||||
*
|
||||
* @return The winner or nobody in case of a tie.
|
||||
*/
|
||||
Player getWinner();
|
||||
|
||||
/**
|
||||
* Get the number of human tiles currently placed on the grid.
|
||||
*
|
||||
* @return The number of human tiles.
|
||||
*/
|
||||
int getNumberOfHumanTiles();
|
||||
|
||||
/**
|
||||
* Get the number of machine tiles currently placed on the grid.
|
||||
*
|
||||
* @return The number of machine tiles.
|
||||
*/
|
||||
int getNumberOfMachineTiles();
|
||||
|
||||
/**
|
||||
* Get the content of the slot at the specified coordinates. Either it
|
||||
* contains a tile of one of the two players already or it is empty.
|
||||
*
|
||||
* @param row The row of the slot in the game grid.
|
||||
* @param col The column of the slot in the game grid.
|
||||
* @return The slot's content.
|
||||
*/
|
||||
Player getSlot(int row, int col);
|
||||
|
||||
/**
|
||||
* Deep-copy the board.
|
||||
*
|
||||
* @return A clone.
|
||||
*/
|
||||
Board clone();
|
||||
|
||||
/**
|
||||
* Get the string representation of this board as row x column matrix. Each
|
||||
* slot is represented by one the three chars '.', 'X', or 'O'. '.' means
|
||||
* that the slot currently contains no tile. 'X' means that it contains a
|
||||
* tile of the human player. 'O' means that it contains a machine tile. In
|
||||
* contrast to the rows, the columns are whitespace separated.
|
||||
*
|
||||
* @return The string representation of the current Reversi game.
|
||||
*/
|
||||
@Override
|
||||
String toString();
|
||||
|
||||
}
|
||||
42
Reversi/Field.java
Normal file
42
Reversi/Field.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package reversi.model;
|
||||
|
||||
/**
|
||||
* Class for a reversi field.
|
||||
*/
|
||||
public class Field {
|
||||
|
||||
/**
|
||||
* <code>Row</code> of the field.
|
||||
*/
|
||||
private int row;
|
||||
|
||||
/**
|
||||
* <code>Column</code> of the field.
|
||||
*/
|
||||
private int col;
|
||||
|
||||
/**
|
||||
* Constructs a new field.
|
||||
*
|
||||
* @param row row of the field
|
||||
* @param col column of the field
|
||||
*/
|
||||
public Field(final int row, final int col) {
|
||||
this.row = row;
|
||||
this.col = col;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @return The row of the field.
|
||||
*/
|
||||
public final int row() {
|
||||
return row;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @return the column of the field.
|
||||
*/
|
||||
public final int col() {
|
||||
return col;
|
||||
}
|
||||
}
|
||||
164
Reversi/GameNode.java
Normal file
164
Reversi/GameNode.java
Normal file
@@ -0,0 +1,164 @@
|
||||
package reversi.model;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Class for a gamenode. Is needed to build a gametree for e.g. minmax
|
||||
* algorithm. A node represents a virtual move.
|
||||
*
|
||||
*/
|
||||
public class GameNode {
|
||||
|
||||
/**
|
||||
* Reversi-board of the node.
|
||||
*/
|
||||
private Reversi state;
|
||||
|
||||
/**
|
||||
* Scoring of the node.
|
||||
*/
|
||||
private double score;
|
||||
|
||||
/**
|
||||
* The Move.
|
||||
*/
|
||||
private Field move;
|
||||
|
||||
/**
|
||||
* Children of the node.
|
||||
*/
|
||||
private LinkedList<GameNode> children;
|
||||
|
||||
/**
|
||||
* Constructor for the root node.
|
||||
*
|
||||
* @param move Root move
|
||||
* @param state Board of the root node
|
||||
*/
|
||||
GameNode(final Field move, final Reversi state) {
|
||||
this.move = move;
|
||||
this.state = state;
|
||||
this.children = new LinkedList<GameNode>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for node.
|
||||
*
|
||||
* @param move Move of node
|
||||
* @param state Board of node
|
||||
* @param parent Parent of node
|
||||
*/
|
||||
GameNode(final Field move, final Reversi state, final GameNode parent) {
|
||||
this(move, state);
|
||||
parent.children.add(this);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param score Score to set
|
||||
*/
|
||||
public final void setScore(final double score) {
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Score
|
||||
*/
|
||||
public final double getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Board
|
||||
*/
|
||||
public final Reversi getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Move
|
||||
*/
|
||||
public final Field getMove() {
|
||||
return move;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Children
|
||||
*/
|
||||
public final LinkedList<GameNode> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is needed to calculate the combined score of the parent node.
|
||||
*
|
||||
* @return GameNode with highest score
|
||||
*/
|
||||
public final GameNode getChildrensHighestScore() {
|
||||
GameNode highest = children.getFirst();
|
||||
|
||||
for (GameNode node : children) {
|
||||
if (node.getScore() > highest.getScore()) {
|
||||
highest = node;
|
||||
}
|
||||
}
|
||||
return highest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is needed to calculate the combined score of the parent node.
|
||||
*
|
||||
* @return GameNode with lowest score
|
||||
*/
|
||||
public final GameNode getChildrensLowestScore() {
|
||||
GameNode lowest = children.getFirst();
|
||||
|
||||
for (GameNode node : children) {
|
||||
if (node.getScore() < lowest.getScore()) {
|
||||
lowest = node;
|
||||
}
|
||||
}
|
||||
return lowest;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return If node has at least one child
|
||||
*/
|
||||
public final boolean hasChild() {
|
||||
return (children.size() > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set children of a node.
|
||||
*/
|
||||
public final void fillNode() {
|
||||
for (Field move : generateChildren()) {
|
||||
Reversi tmpBoard = this.getState().clone();
|
||||
tmpBoard.performMove(move.row(), move.col());
|
||||
new GameNode(move, tmpBoard, this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return <code>Linkedlist</code> with possible moves in the
|
||||
* current reversi-board.
|
||||
*/
|
||||
private LinkedList<Field> generateChildren() {
|
||||
LinkedList<Field> movesList = new LinkedList<Field>();
|
||||
|
||||
for (int row = 0; row < Board.SIZE; row++) {
|
||||
for (int col = 0; col < Board.SIZE; col++) {
|
||||
if (getState().isValidMove(row, col)) {
|
||||
movesList.add(new Field(row, col));
|
||||
}
|
||||
}
|
||||
}
|
||||
return movesList;
|
||||
}
|
||||
}
|
||||
75
Reversi/GameTree.java
Normal file
75
Reversi/GameTree.java
Normal file
@@ -0,0 +1,75 @@
|
||||
package reversi.model;
|
||||
|
||||
/**
|
||||
* Class for a GameTree.
|
||||
*/
|
||||
public class GameTree {
|
||||
|
||||
/**
|
||||
* Root gamenode of the gametree.
|
||||
*/
|
||||
private GameNode root;
|
||||
|
||||
/**
|
||||
* Constructs a new gametree.
|
||||
*
|
||||
* @param board of the root node
|
||||
* @param level depth of the tree
|
||||
*/
|
||||
GameTree(final Reversi board, final int level) {
|
||||
this.root = new GameNode(new Field(0, 0), board.clone());
|
||||
fillTree(root, level);
|
||||
generateScores(root);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Root of the gametree.
|
||||
*/
|
||||
public final GameNode getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill the Tree with nodes.
|
||||
*
|
||||
* @param node Node to fill (in most cases root gamenode)
|
||||
* @param depth depth of the tree (level)
|
||||
*/
|
||||
private void fillTree(final GameNode node, final int depth) {
|
||||
if (depth > 0) {
|
||||
node.fillNode();
|
||||
|
||||
for (GameNode n : node.getChildren()) {
|
||||
fillTree(n, (depth - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and sets the score of a Gamenode.
|
||||
*
|
||||
* @param node Gamenode to set
|
||||
*/
|
||||
public final void generateScores(final GameNode node) {
|
||||
Reversi currentBoard = node.getState();
|
||||
|
||||
if (!node.hasChild()) {
|
||||
node.setScore(currentBoard.score());
|
||||
} else {
|
||||
double score;
|
||||
score = currentBoard.score();
|
||||
|
||||
for (GameNode child : node.getChildren()) {
|
||||
generateScores(child);
|
||||
}
|
||||
|
||||
if (!currentBoard.next().getType()) {
|
||||
score += node.getChildrensHighestScore().getScore();
|
||||
} else {
|
||||
score += node.getChildrensLowestScore().getScore();
|
||||
}
|
||||
node.setScore(score);
|
||||
}
|
||||
}
|
||||
}
|
||||
31
Reversi/IllegalMoveException.java
Normal file
31
Reversi/IllegalMoveException.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package reversi.model;
|
||||
|
||||
import java.awt.Toolkit;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
/**
|
||||
* Class for a Illegal move in reversi game.
|
||||
*/
|
||||
public class IllegalMoveException extends RuntimeException {
|
||||
|
||||
private static final long serialVersionUID = 240330120795325167L;
|
||||
|
||||
/**
|
||||
* Prints an error or plays a "beep" if a illegal move is made.
|
||||
* A beep is more common but not so easy to implement for various
|
||||
* OS, so the sound is only played on Windows XP or 7.
|
||||
*
|
||||
* @param s Error to print
|
||||
*/
|
||||
public IllegalMoveException(final String s) {
|
||||
if (System.getProperty("os.name").equals("Windows 7")
|
||||
|| System.getProperty("os.name").equals("Windows XP")) {
|
||||
Toolkit.getDefaultToolkit().beep();
|
||||
} else {
|
||||
JFrame screen = new JFrame();
|
||||
JOptionPane.showMessageDialog(screen, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
42
Reversi/MachineMoveThread.java
Normal file
42
Reversi/MachineMoveThread.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package reversi.controller;
|
||||
|
||||
import reversi.model.Player;
|
||||
import reversi.model.Reversi;
|
||||
import reversi.view.ReversiView;
|
||||
|
||||
/**
|
||||
* Creates a new thread for the machine move.
|
||||
*/
|
||||
public class MachineMoveThread extends Thread {
|
||||
|
||||
private Reversi model;
|
||||
private ReversiView view;
|
||||
private Player bot;
|
||||
|
||||
MachineMoveThread(final Reversi model, final ReversiView view) {
|
||||
this.model = model;
|
||||
this.view = view;
|
||||
this.bot = model.next();
|
||||
}
|
||||
@Override
|
||||
public final void run() {
|
||||
this.model.machineMove();
|
||||
javax.swing.SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public final void run() {
|
||||
view.updatePlayField(model);
|
||||
view.setMovePossible(true);
|
||||
}
|
||||
});
|
||||
|
||||
if (model.next() == bot && !model.gameOver()) {
|
||||
this.run();
|
||||
javax.swing.SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public final void run() {
|
||||
view.viewMessage("Human has to miss a turn");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
27
Reversi/Player.java
Normal file
27
Reversi/Player.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package reversi.model;
|
||||
/**
|
||||
* Class for a player of reversi.
|
||||
*/
|
||||
public class Player {
|
||||
/**
|
||||
* Indicates if a player is human.
|
||||
*/
|
||||
private Boolean isHuman;
|
||||
|
||||
/**
|
||||
* Constructs a new player.
|
||||
*
|
||||
* @param isHuman is the player human?
|
||||
*/
|
||||
public Player(final boolean isHuman) {
|
||||
this.isHuman = isHuman;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Is player human?
|
||||
*/
|
||||
public final boolean getType() {
|
||||
return isHuman;
|
||||
}
|
||||
}
|
||||
527
Reversi/Reversi.java
Normal file
527
Reversi/Reversi.java
Normal file
@@ -0,0 +1,527 @@
|
||||
package reversi.model;
|
||||
|
||||
/**
|
||||
* Class for reversi game. A human player tries to beat a bot with modifiable
|
||||
* difficulty level.
|
||||
*/
|
||||
public class Reversi implements Board {
|
||||
|
||||
private Player bot;
|
||||
private Player human;
|
||||
|
||||
/**
|
||||
* Player with the next move.
|
||||
*/
|
||||
private Player currentPlayer;
|
||||
private Player firstPlayer;
|
||||
|
||||
/**
|
||||
* Playboard saves the tiles.
|
||||
*/
|
||||
private Player[][] playBoard;
|
||||
|
||||
/**
|
||||
* Gametree for the bot to calculate future moves.
|
||||
*/
|
||||
private GameTree gameTree;
|
||||
|
||||
/**
|
||||
* Vectors to traverse the 8 directions around a tile.
|
||||
*/
|
||||
private Field[] vectors;
|
||||
|
||||
/**
|
||||
* Saves if game is over.
|
||||
*/
|
||||
private boolean gameOver;
|
||||
|
||||
/**
|
||||
* Standard weighting of the playboard. Used by the bot to calculate
|
||||
* gametree.
|
||||
*/
|
||||
private double[][] weighting = new double[][]
|
||||
{{9999, 5, 500, 200, 200, 500, 5, 9999},
|
||||
{5, 1, 50, 150, 150, 50, 1, 5},
|
||||
{500, 50, 250, 100, 100, 250, 50, 500},
|
||||
{200, 150, 100, 50, 50, 100, 150, 200},
|
||||
{200, 150, 100, 50, 50, 100, 150, 200},
|
||||
{500, 50, 250, 100, 100, 250, 50, 500},
|
||||
{5, 1, 50, 150, 150, 50, 1, 5},
|
||||
{9999, 5, 500, 200, 200, 500, 5, 9999}};
|
||||
|
||||
/**
|
||||
* Difficulty level for the human. Represents the depth of the gametree.
|
||||
*/
|
||||
private int level = 3;
|
||||
|
||||
/**
|
||||
* Constructs a new reversi game.
|
||||
*
|
||||
* @param human Human player
|
||||
* @param bot Machine player
|
||||
* @param firstPlayer Player that starts
|
||||
*/
|
||||
public Reversi(Player human, Player bot, Player firstPlayer) {
|
||||
this.bot = bot;
|
||||
this.human = human;
|
||||
this.firstPlayer = firstPlayer;
|
||||
this.playBoard = setInitialPositions();
|
||||
currentPlayer = firstPlayer;
|
||||
this.vectors = generateVectors();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final Player getFirstPlayer() {
|
||||
return firstPlayer;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final Player next() {
|
||||
return currentPlayer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final boolean move(final int row, final int col) {
|
||||
if (currentPlayer != human) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isValidMove(row, col)) {
|
||||
new IllegalMoveException("Error! invalid move at ("
|
||||
+ (row + 1) + ", " + (col + 1) + ").");
|
||||
return false;
|
||||
}
|
||||
performMove(row, col);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final void machineMove() {
|
||||
if (currentPlayer != bot) {
|
||||
return;
|
||||
}
|
||||
gameTree = new GameTree(this, level);
|
||||
Field bestMove = gameTree.getRoot()
|
||||
.getChildrensHighestScore().getMove();
|
||||
|
||||
performMove(bestMove.row(), bestMove.col());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final void setLevel(final int level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final boolean gameOver() {
|
||||
return gameOver;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final Player getWinner() {
|
||||
if (getNumberOfHumanTiles() > getNumberOfMachineTiles()) {
|
||||
return human;
|
||||
} else if (getNumberOfHumanTiles() == getNumberOfMachineTiles()) {
|
||||
return null;
|
||||
} else {
|
||||
return bot;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final int getNumberOfHumanTiles() {
|
||||
return numberOfTiles(human);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final int getNumberOfMachineTiles() {
|
||||
return numberOfTiles(bot);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final int getNumberOfTilesSet() {
|
||||
int n = (getNumberOfHumanTiles() + getNumberOfMachineTiles());
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public final Player getSlot(final int row, final int col) {
|
||||
return playBoard[row][col];
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Reversi clone() {
|
||||
Reversi boardCopy = new Reversi(human, bot, firstPlayer);
|
||||
boardCopy.currentPlayer = currentPlayer;
|
||||
Player[][] playBoardCopy = new Player[SIZE][SIZE];
|
||||
|
||||
for (int row = 0; row < SIZE; row++) {
|
||||
for (int col = 0; col < SIZE; col++) {
|
||||
playBoardCopy[row][col] = playBoard[row][col];
|
||||
}
|
||||
}
|
||||
boardCopy.playBoard = playBoardCopy;
|
||||
return boardCopy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the vectors for the 8 directions. array[0] = 0°,
|
||||
* array[1] = 45°...
|
||||
*
|
||||
* @return <code>boolean-array</code> with all vector fields
|
||||
*/
|
||||
private Field[] generateVectors() {
|
||||
Field[] vector = {
|
||||
new Field(-1, 0),
|
||||
new Field(-1, +1),
|
||||
new Field(0, +1),
|
||||
new Field(+1, +1),
|
||||
new Field(+1, 0),
|
||||
new Field(+1, -1),
|
||||
new Field(0, -1),
|
||||
new Field(-1, -1)};
|
||||
return vector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets two tiles/player at the beginning of the match in the middle of
|
||||
* of the playboard.
|
||||
*
|
||||
* @return <code>Player-array</code>
|
||||
*/
|
||||
private Player[][] setInitialPositions() {
|
||||
Player[][] playBoard = new Player[SIZE][SIZE];
|
||||
|
||||
for (int row = 0; row < SIZE; row++) {
|
||||
for (int col = 0; col < SIZE; col++) {
|
||||
if (((SIZE / 2) - 1 == row || SIZE / 2 == row)
|
||||
&& ((SIZE - col - 1) == row)) {
|
||||
playBoard[row][col] = firstPlayer;
|
||||
} else if ((SIZE / 2 - 1 == row || SIZE / 2 == row)
|
||||
&& (col == row)) {
|
||||
playBoard[row][col] = getEnemy(firstPlayer);
|
||||
} else {
|
||||
playBoard[row][col] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return playBoard;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param self Player
|
||||
* @return Enemy of the Player
|
||||
*/
|
||||
private Player getEnemy(final Player self) {
|
||||
if (self == human) {
|
||||
return bot;
|
||||
} else {
|
||||
return human;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches the currentplayer and checks if the next player can do a valid
|
||||
* move. If both players cannot move the game is over.
|
||||
* game is over
|
||||
*/
|
||||
private void switchPlayer() {
|
||||
currentPlayer = getEnemy(currentPlayer);
|
||||
|
||||
if (!movePossible()) {
|
||||
currentPlayer = getEnemy(currentPlayer);
|
||||
|
||||
if (!movePossible()) {
|
||||
gameOver = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch a tile to the current player.
|
||||
*
|
||||
* @param row Row of field
|
||||
* @param col Column of field
|
||||
*/
|
||||
private void switchTile(final int row, final int col) {
|
||||
playBoard[row][col] = currentPlayer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch all corresponding enemy tiles around a <code>field</code>.
|
||||
*
|
||||
* @param row Row of field
|
||||
* @param col Column of field
|
||||
*/
|
||||
private void switchTiles(final int row, final int col) {
|
||||
boolean[] surrounders = validMoves(row, col);
|
||||
switchTile(row, col);
|
||||
|
||||
for (int i = 0; i < SIZE; i++) {
|
||||
if (surrounders[i]) {
|
||||
int x = row + vectors[i].row();
|
||||
int y = col + vectors[i].col();
|
||||
|
||||
while (playBoard[x][y] != currentPlayer) {
|
||||
switchTile(x, y);
|
||||
x = x + vectors[i].row();
|
||||
y = y + vectors[i].col();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int numberOfTiles(Player player) {
|
||||
int mount = 0;
|
||||
|
||||
for (int row = 0; row < SIZE; row++) {
|
||||
for (int col = 0; col < SIZE; col++) {
|
||||
if (playBoard[row][col] == player) {
|
||||
mount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return mount;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param row Row of the field
|
||||
* @param col Column of the field
|
||||
* @return <code>true</code> if field with <code>row</code> and
|
||||
* <code>col</code> is not set by one of the
|
||||
* player <br /> else if field is set
|
||||
*/
|
||||
private boolean isFree(final int row, final int col) {
|
||||
if (playBoard[row][col] == null) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the hole playBoard if the current player can do a valid move.
|
||||
*
|
||||
* @return <code>true</code> if another move is possible.
|
||||
*/
|
||||
private boolean movePossible() {
|
||||
for (int row = 0; row < SIZE; row++) {
|
||||
for (int col = 0; col < SIZE; col++) {
|
||||
|
||||
if (isValidMove(row, col)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walk the board in a direction.
|
||||
*
|
||||
* @param row Row of field
|
||||
* @param col Column of field
|
||||
* @param x row direction to walk
|
||||
* @param y column direction to walk
|
||||
* @return if the direction has a valid move
|
||||
*/
|
||||
private boolean boardWalker(int row, int col, final int x, final int y) {
|
||||
row = row + x;
|
||||
col = col + y;
|
||||
int counter = 0;
|
||||
|
||||
while (row < SIZE && row >= 0 && col < SIZE && col >= 0) {
|
||||
if (playBoard[row][col] == null) {
|
||||
return false;
|
||||
} else if (playBoard[row][col] == currentPlayer) {
|
||||
|
||||
return (counter > 0);
|
||||
}
|
||||
row = row + x;
|
||||
col = col + y;
|
||||
counter++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks a boolean array if it contains a value that is true.
|
||||
*
|
||||
* @param array Array
|
||||
* @return <code>true</code> if any value is true
|
||||
*/
|
||||
private boolean checkBoolArray(final boolean[] array) {
|
||||
for (int i = 0; i < array.length; i++) {
|
||||
if (array[i]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param row Row of field
|
||||
* @param col Column of field
|
||||
* @return if the currentPlayer can do a move on field(row, col)
|
||||
*/
|
||||
final boolean isValidMove(final int row, final int col) {
|
||||
return isFree(row, col)
|
||||
&& checkBoolArray(validMoves(row, col));
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches a field for valid moves.
|
||||
*
|
||||
* @param row Row of field
|
||||
* @param col Column of field
|
||||
* @return <code>Array</code> of enemy tiles: <br />
|
||||
* array[0] is at 0°, array[1] is diagonal 45°,<br />
|
||||
* array[2] right 90° and so on
|
||||
*/
|
||||
private boolean[] validMoves(final int row, final int col) {
|
||||
boolean[] neighbourhood = new boolean[8];
|
||||
|
||||
for (int i = 0; i < neighbourhood.length; i++) {
|
||||
neighbourhood[i] = boardWalker(row, col,
|
||||
vectors[i].row(), vectors[i].col());
|
||||
}
|
||||
return neighbourhood;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switches all tiles and changes switches player if possible.
|
||||
*
|
||||
* @param row Row of field
|
||||
* @param col Column of field
|
||||
*/
|
||||
void performMove(final int row, final int col) {
|
||||
switchTiles(row, col);
|
||||
switchPlayer();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the score of a reversi.
|
||||
*
|
||||
* @return <code>score</code>
|
||||
*/
|
||||
double score() {
|
||||
double score = (scoreT() + scoreM() + scoreP());
|
||||
return score;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the weighting score.
|
||||
*
|
||||
* @return <code>scoreT</code>
|
||||
*/
|
||||
private double scoreT() {
|
||||
double scoreSelf = 0;
|
||||
double scoreEnemy = 0;
|
||||
|
||||
for (int row = 0; row < SIZE; row++) {
|
||||
for (int col = 0; col < SIZE; col++) {
|
||||
if (playBoard[row][col] == bot) {
|
||||
scoreSelf += weighting[row][col];
|
||||
} else if (playBoard[row][col] == human) {
|
||||
scoreEnemy += weighting[row][col];
|
||||
}
|
||||
}
|
||||
}
|
||||
return (scoreSelf - (1.5 * scoreEnemy));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the mobility score.
|
||||
*
|
||||
* @return <code>scoreM</code>
|
||||
*/
|
||||
private double scoreM() {
|
||||
double scoreSelf = 0;
|
||||
double scoreEnemy = 0;
|
||||
Player tmp = currentPlayer;
|
||||
int n = getNumberOfTilesSet();
|
||||
|
||||
for (int row = 0; row < SIZE; row++) {
|
||||
for (int col = 0; col < SIZE; col++) {
|
||||
currentPlayer = bot;
|
||||
|
||||
if (isValidMove(row, col)) {
|
||||
scoreSelf++;
|
||||
}
|
||||
currentPlayer = human;
|
||||
|
||||
if (isValidMove(row, col)) {
|
||||
scoreEnemy++;
|
||||
}
|
||||
currentPlayer = bot;
|
||||
}
|
||||
}
|
||||
currentPlayer = tmp;
|
||||
return (64.0 / n) * (3.0 * scoreSelf - 4.0 * scoreEnemy);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the future-potential score.
|
||||
*
|
||||
* @return <code>scoreP</code>
|
||||
*/
|
||||
private double scoreP() {
|
||||
double scoreSelf = 0;
|
||||
double scoreEnemy = 0;
|
||||
int n = getNumberOfTilesSet();
|
||||
|
||||
for (int row = 0; row < SIZE; row++) {
|
||||
for (int col = 0; col < SIZE; col++) {
|
||||
if (playBoard[row][col] == human) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int x = row + vectors[i].row();
|
||||
int y = col + vectors[i].col();
|
||||
|
||||
if ((x >= 0 && x < SIZE && y >= 0 && y < SIZE)
|
||||
&& (playBoard[x][y] == null)) {
|
||||
scoreSelf++;
|
||||
}
|
||||
}
|
||||
} else if (playBoard[row][col] == bot) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
int x = row + vectors[i].row();
|
||||
int y = col + vectors[i].col();
|
||||
|
||||
if ((x >= 0 && x < SIZE && y >= 0 && y < SIZE)
|
||||
&& (playBoard[x][y] == null)) {
|
||||
scoreEnemy++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return (64.0 / (2 * n)) * (2.5 * scoreSelf - 3.0 * scoreEnemy);
|
||||
}
|
||||
}
|
||||
237
Reversi/ReversiController.java
Normal file
237
Reversi/ReversiController.java
Normal file
@@ -0,0 +1,237 @@
|
||||
package reversi.controller;
|
||||
|
||||
import java.awt.Frame;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.Stack;
|
||||
|
||||
import javax.swing.JComboBox;
|
||||
|
||||
import reversi.model.Board;
|
||||
import reversi.model.Field;
|
||||
import reversi.model.IllegalMoveException;
|
||||
import reversi.model.Player;
|
||||
import reversi.model.Reversi;
|
||||
import reversi.view.ReversiField;
|
||||
import reversi.view.ReversiView;
|
||||
|
||||
/**
|
||||
* Controller has an instance of the board and handles the
|
||||
* mouse and keyboard actions. Updates the gui if playBoard changes.
|
||||
*/
|
||||
public class ReversiController {
|
||||
|
||||
private Player human = new Player(true);
|
||||
private Player bot = new Player(false);
|
||||
private Reversi model;
|
||||
private ReversiView view;
|
||||
private MachineMoveThread machineMoveThread;
|
||||
|
||||
/**
|
||||
* Stack for UndoMove method.
|
||||
*/
|
||||
private Stack<Reversi> pastMoves;
|
||||
|
||||
/**
|
||||
* Create the model and the view.
|
||||
*/
|
||||
public ReversiController() {
|
||||
this.model = new Reversi(human, bot, human);
|
||||
this.view = new ReversiView();
|
||||
addListener();
|
||||
this.view.updatePlayField(this.model);
|
||||
createStack();
|
||||
}
|
||||
|
||||
/**
|
||||
* View the gui.
|
||||
*/
|
||||
public final void showView() {
|
||||
this.view.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a new reversi game.
|
||||
*
|
||||
* @param firstPlayer Player that starts
|
||||
* @return new reversi board
|
||||
*/
|
||||
private Reversi newGame(Player firstPlayer) {
|
||||
|
||||
stopMachineMoveThread();
|
||||
createStack();
|
||||
Reversi model = new Reversi(this.human, this.bot, firstPlayer);
|
||||
model.setLevel(this.view.getLevel());
|
||||
|
||||
if (firstPlayer == bot) {
|
||||
performMachineMove(model);
|
||||
} else {
|
||||
this.view.updatePlayField(model);
|
||||
this.view.setMovePossible(true);
|
||||
}
|
||||
return model;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add all Listener in the gui.
|
||||
*/
|
||||
private void addListener() {
|
||||
setPlayFieldListeners();
|
||||
this.view.setNewGameListener(new NewGameListener());
|
||||
this.view.setSwitchLevelListener(new SwitchLevelListener());
|
||||
this.view.setSwitchGameListener(new SwitchGameListener());
|
||||
this.view.setUndoMoveListener(new UndoMoveListener());
|
||||
this.view.setQuitGameListener(new QuitGameListener());
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the MouseListener for each PlayField.
|
||||
*/
|
||||
private void setPlayFieldListeners() {
|
||||
for (int row = 0; row < Board.SIZE; row++) {
|
||||
for (int col = 0; col < Board.SIZE; col++) {
|
||||
this.view.setPlayFieldListener(
|
||||
new FieldListener(), new Field(row, col));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Init Stack and disable Undo Button.
|
||||
*/
|
||||
private void createStack() {
|
||||
this.pastMoves = new Stack<Reversi>();
|
||||
view.setUndoButtonVisible(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return to the state of the board before the last human and bot move.
|
||||
*/
|
||||
private void undoMove() {
|
||||
stopMachineMoveThread();
|
||||
this.model = pastMoves.pop();
|
||||
this.view.updatePlayField(this.model);
|
||||
|
||||
if (pastMoves.isEmpty()) {
|
||||
this.view.setUndoButtonVisible(false);
|
||||
} else {
|
||||
this.view.setMovePossible(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void performMachineMove(Reversi model) {
|
||||
this.view.setMovePossible(false);
|
||||
machineMoveThread = new MachineMoveThread(model, this.view);
|
||||
machineMoveThread.start();
|
||||
}
|
||||
|
||||
private void performHumanMove(Field field) {
|
||||
pastMoves.push(model.clone());
|
||||
view.setUndoButtonVisible(true);
|
||||
|
||||
if (model.move(field.row(), field.col())) {
|
||||
if (model.next() == human && !model.gameOver()) {
|
||||
javax.swing.SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public final void run() {
|
||||
view.viewMessage("The bot has to miss a turn");
|
||||
}
|
||||
});
|
||||
}
|
||||
} else {
|
||||
undoMove();
|
||||
}
|
||||
|
||||
view.updatePlayField(model);
|
||||
|
||||
if (model.next() == bot) {
|
||||
performMachineMove(model);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void stopMachineMoveThread() {
|
||||
if (machineMoveThread != null) {
|
||||
machineMoveThread.stop();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class FieldListener performs a move on click and starts the
|
||||
* machine move. After every move, game over check is done.
|
||||
*/
|
||||
private class FieldListener extends MouseAdapter {
|
||||
@Override
|
||||
public void mousePressed(final MouseEvent event) {
|
||||
if (!view.getMovePossible()) {
|
||||
new IllegalMoveException("Machine Move is active");
|
||||
return;
|
||||
}
|
||||
|
||||
Field field = ((ReversiField) event.getComponent()).getField();
|
||||
performHumanMove(field);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Listener for the level switch combobox.
|
||||
*/
|
||||
private class SwitchLevelListener implements ActionListener {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
@SuppressWarnings("unchecked")
|
||||
JComboBox<Integer> level = (JComboBox<Integer>) e.getSource();
|
||||
model.setLevel((Integer) level.getSelectedItem());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Listener for the new game button.
|
||||
*/
|
||||
private class NewGameListener implements ActionListener {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
model = newGame(model.getFirstPlayer());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Listener for the switch game button.
|
||||
*/
|
||||
private class SwitchGameListener implements ActionListener {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
|
||||
if (model.getFirstPlayer() == human) {
|
||||
model = newGame(bot);
|
||||
} else {
|
||||
model = newGame(human);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Listener for the undo move button.
|
||||
*/
|
||||
private class UndoMoveListener implements ActionListener {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
undoMove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Listener for the quit game button.
|
||||
*/
|
||||
private class QuitGameListener implements ActionListener {
|
||||
@Override
|
||||
public void actionPerformed(final ActionEvent e) {
|
||||
for (Frame frame : java.awt.Frame.getFrames()) {
|
||||
frame.dispose();
|
||||
}
|
||||
stopMachineMoveThread();
|
||||
}
|
||||
}
|
||||
}
|
||||
83
Reversi/ReversiField.java
Normal file
83
Reversi/ReversiField.java
Normal file
@@ -0,0 +1,83 @@
|
||||
package reversi.view;
|
||||
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.event.MouseAdapter;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import reversi.model.Field;
|
||||
import reversi.model.Player;
|
||||
|
||||
/**
|
||||
* Class for a single play field panel. Paints the circles with the color of
|
||||
* the Player.
|
||||
*/
|
||||
public class ReversiField extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = -5762403586769495603L;
|
||||
private int height;
|
||||
private int width;
|
||||
private int y;
|
||||
private int x;
|
||||
private final Field field;
|
||||
private Player player;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param field <code>Field</code> with coordinates of the play field.
|
||||
*/
|
||||
public ReversiField(final Field field) {
|
||||
this.field = field;
|
||||
setPlayer(player);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public final void paintComponent(final Graphics g) {
|
||||
super.paintComponent(g);
|
||||
|
||||
height = (int) (super.getHeight() * 0.8);
|
||||
width = (int) (super.getWidth() * 0.8);
|
||||
y = (int) (width * 0.1 + 2);
|
||||
x = (int) (height * 0.1 + 2);
|
||||
|
||||
if (player != null && player.getType()) {
|
||||
g.setColor(Color.BLUE);
|
||||
g.fillOval(y, x, width, height);
|
||||
} else if (player != null && !player.getType()) {
|
||||
g.setColor(Color.RED);
|
||||
g.fillOval(y, x, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return <code>Field</code> with coordinates of the play field.
|
||||
*/
|
||||
public final Field getField() {
|
||||
return this.field;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the listener created in the controller.
|
||||
*
|
||||
* @param ma Mouseadapter
|
||||
*/
|
||||
public final void setListener(final MouseAdapter ma) {
|
||||
this.addMouseListener(ma);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a player on a field (draw the circle in right color).
|
||||
*
|
||||
* @param player Player that is set.
|
||||
*/
|
||||
public void setPlayer(Player player) {
|
||||
this.player = player;
|
||||
repaint();
|
||||
}
|
||||
}
|
||||
24
Reversi/ReversiGame.java
Normal file
24
Reversi/ReversiGame.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package reversi.main;
|
||||
|
||||
import reversi.controller.ReversiController;
|
||||
|
||||
/**
|
||||
* Starts a Reversi Game with the gui.
|
||||
*/
|
||||
public final class ReversiGame {
|
||||
|
||||
private ReversiController controller;
|
||||
|
||||
private ReversiGame() {
|
||||
this.controller = new ReversiController();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param args unused...
|
||||
*/
|
||||
public static void main(final String...args) {
|
||||
ReversiGame reversiGame = new ReversiGame();
|
||||
reversiGame.controller.showView();
|
||||
}
|
||||
}
|
||||
70
Reversi/ReversiPlayBoard.java
Normal file
70
Reversi/ReversiPlayBoard.java
Normal file
@@ -0,0 +1,70 @@
|
||||
package reversi.view;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.event.MouseAdapter;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.border.MatteBorder;
|
||||
|
||||
import reversi.model.Board;
|
||||
import reversi.model.Field;
|
||||
import reversi.model.Reversi;
|
||||
|
||||
/**
|
||||
* Create a reversi playboard for the reversi game gui.
|
||||
*/
|
||||
public class ReversiPlayBoard extends JPanel {
|
||||
|
||||
private static final long serialVersionUID = 2725183063551812386L;
|
||||
private ReversiField[][] playField = new ReversiField
|
||||
[Board.SIZE][Board.SIZE];
|
||||
|
||||
/**
|
||||
* Init array with all reversi fields. Set background and tooltip of
|
||||
* fields.
|
||||
*/
|
||||
public ReversiPlayBoard() {
|
||||
this.setLayout(new GridLayout(Board.SIZE, Board.SIZE));
|
||||
this.setBorder(new CompoundBorder(
|
||||
null, new EmptyBorder(0, 0, 10, 10)));
|
||||
|
||||
for (int row = 0; row < Board.SIZE; row++) {
|
||||
for (int col = 0; col < Board.SIZE; col++) {
|
||||
playField[row][col] = new ReversiField(new Field(row, col));
|
||||
playField[row][col].setBorder(
|
||||
new MatteBorder(1, 1, 0, 0, Color.BLACK));
|
||||
playField[row][col].setBackground(new Color(0, 170, 0));
|
||||
playField[row][col].setToolTipText("Click to make a move!");
|
||||
this.add(playField[row][col]);
|
||||
}
|
||||
}
|
||||
this.setVisible(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update hole playboard players.
|
||||
*
|
||||
* @param board Board to update.
|
||||
*/
|
||||
public void updatePlayBoard(Reversi board) {
|
||||
for (int row = 0; row < Board.SIZE; row++) {
|
||||
for (int col = 0; col < Board.SIZE; col++) {
|
||||
this.playField[row][col].setPlayer(board.getSlot(row, col));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set mouselistener of <code>field</code>
|
||||
*
|
||||
* @param ma Mouseadapter
|
||||
* @param field Field
|
||||
*/
|
||||
public final void setFieldListener(
|
||||
final MouseAdapter ma, final Field field) {
|
||||
this.playField[field.row()][field.col()].setListener(ma);
|
||||
}
|
||||
}
|
||||
292
Reversi/ReversiView.java
Normal file
292
Reversi/ReversiView.java
Normal file
@@ -0,0 +1,292 @@
|
||||
package reversi.view;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Container;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Font;
|
||||
import java.awt.GridLayout;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JComboBox;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import reversi.model.Board;
|
||||
import reversi.model.Field;
|
||||
import reversi.model.Reversi;
|
||||
|
||||
/**
|
||||
* Create and update the gui.
|
||||
*/
|
||||
public class ReversiView extends JFrame {
|
||||
|
||||
private static final long serialVersionUID = 5382834862595792805L;
|
||||
private boolean movePossible;
|
||||
private ReversiPlayBoard playBoard;
|
||||
private JPanel horizTableLabel;
|
||||
private JPanel verticTableLabel;
|
||||
private JLabel humanTiles;
|
||||
private JLabel botTiles;
|
||||
private JComboBox<Integer> switchLevel =
|
||||
new JComboBox<Integer>(new Integer[] {1, 2, 3, 4, 5});
|
||||
private JButton newGame = new JButton("New");
|
||||
private JButton switchGame = new JButton("Switch");
|
||||
private JButton undoMove = new JButton("Undo");
|
||||
private JButton quitGame = new JButton("Quit");
|
||||
|
||||
/**
|
||||
* Create the gui for the reversi game.
|
||||
*/
|
||||
public ReversiView() {
|
||||
super("Reversi");
|
||||
initForms();
|
||||
movePossible = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create all buttons and the playfield.
|
||||
*/
|
||||
private void initForms() {
|
||||
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
this.setSize(410, 450);
|
||||
this.setLayout(new BorderLayout());
|
||||
|
||||
playBoard = new ReversiPlayBoard();
|
||||
verticTableLabel = verticTableLabel();
|
||||
horizTableLabel = horizTableLabel();
|
||||
humanTiles = tilesSet();
|
||||
humanTiles.setForeground(Color.BLUE);
|
||||
botTiles = tilesSet();
|
||||
botTiles.setForeground(Color.RED);
|
||||
|
||||
Container menuBar = new Container();
|
||||
menuBar.setLayout(new FlowLayout());
|
||||
menuBar.add(humanTiles);
|
||||
menuBar.add(switchLevel);
|
||||
menuBar.add(newGame);
|
||||
menuBar.add(switchGame);
|
||||
switchLevel.setSelectedIndex(2);
|
||||
menuBar.add(undoMove);
|
||||
menuBar.add(quitGame);
|
||||
menuBar.add(botTiles);
|
||||
|
||||
|
||||
this.getContentPane().add(playBoard, BorderLayout.CENTER);
|
||||
this.getContentPane().add(menuBar, BorderLayout.SOUTH);
|
||||
this.getContentPane().add(verticTableLabel, BorderLayout.WEST);
|
||||
this.getContentPane().add(horizTableLabel, BorderLayout.NORTH);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the vertical label for the playfield.
|
||||
*
|
||||
* @return JPanel The label.
|
||||
*/
|
||||
private JPanel verticTableLabel() {
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout(new GridLayout(Board.SIZE, 1));
|
||||
panel.setBorder(new CompoundBorder(
|
||||
null, new EmptyBorder(0, 5, 10, 5)));
|
||||
|
||||
for (int i = 1; i <= Board.SIZE; i++) {
|
||||
JLabel tmp = new JLabel(String.valueOf(i));
|
||||
panel.add(tmp);
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the horizontal label for the playfield.
|
||||
*
|
||||
* @return JPanel The label.
|
||||
*/
|
||||
private JPanel horizTableLabel() {
|
||||
JPanel panel = new JPanel();
|
||||
panel.setLayout(new GridLayout(1, Board.SIZE));
|
||||
panel.setBorder(new CompoundBorder(
|
||||
null, new EmptyBorder(0, 20, 0, 12)));
|
||||
|
||||
for (int i = 1; i <= Board.SIZE; i++) {
|
||||
JLabel tmp = new JLabel(String.valueOf(i));
|
||||
tmp.setHorizontalAlignment(SwingConstants.CENTER);
|
||||
panel.add(tmp);
|
||||
}
|
||||
return panel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates both labels for the number of tiles that are set.
|
||||
*
|
||||
* @return JLabel The label.
|
||||
*/
|
||||
private JLabel tilesSet() {
|
||||
JLabel tiles = new JLabel();
|
||||
tiles.setBorder(new CompoundBorder(null, new EmptyBorder(0, 3, 0, 3)));
|
||||
tiles.setFont(new Font(Font.DIALOG, Font.BOLD, 20));
|
||||
return tiles;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return Level
|
||||
*/
|
||||
public final int getLevel() {
|
||||
return (Integer) this.switchLevel.getSelectedItem();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return <code>true</code> if human is allowed to perform a move.<br />
|
||||
* <code>false</code> e.g. if a machine move is active.
|
||||
*/
|
||||
public boolean getMovePossible() {
|
||||
return movePossible;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param movePossible Set if human move is possible.
|
||||
*/
|
||||
public final void setMovePossible(final boolean movePossible) {
|
||||
this.movePossible = movePossible;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param bool Set the undo button vissibility.
|
||||
*/
|
||||
public final void setUndoButtonVisible(final boolean bool) {
|
||||
this.undoMove.setEnabled(bool);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the Tiles display in the gui.
|
||||
*
|
||||
* @param human Human amount of tiles.
|
||||
* @param bot Amount of machine tiles.
|
||||
*/
|
||||
private void setTilesSet(int human, int bot) {
|
||||
this.humanTiles.setText(String.valueOf(human));
|
||||
this.botTiles.setText(String.valueOf(bot));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the players on the playfield.
|
||||
*
|
||||
* @param model Board to update.
|
||||
*/
|
||||
public final void updatePlayField(final Reversi model) {
|
||||
javax.swing.SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public final void run() {
|
||||
playBoard.updatePlayBoard(model);
|
||||
setTilesSet(model.getNumberOfHumanTiles(),
|
||||
model.getNumberOfMachineTiles());
|
||||
|
||||
/* check movePossible just because of thread interference
|
||||
when game is over
|
||||
*/
|
||||
if (model.gameOver() && movePossible) {
|
||||
viewMessage(winnerMessage(model));
|
||||
movePossible = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a message dialog and play a system sound.
|
||||
*
|
||||
* @param msg Message to show.
|
||||
*/
|
||||
public final void viewMessage(final String msg) {
|
||||
Toolkit.getDefaultToolkit().beep();
|
||||
JOptionPane.showMessageDialog(this, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks which player has won.
|
||||
*
|
||||
* @return Message with the winner.
|
||||
*/
|
||||
private String winnerMessage(Reversi model) {
|
||||
if (model.getWinner() == null) {
|
||||
return "Tie game!";
|
||||
} else if (model.getWinner().getType()) {
|
||||
return "You have won!";
|
||||
} else {
|
||||
return "The Computer has won!";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass the field listener of the controller to ReversiPlayBoard class.
|
||||
*
|
||||
* @param ma Mouseadapter
|
||||
* @param field Field
|
||||
*/
|
||||
public final void setPlayFieldListener(
|
||||
final MouseAdapter ma, final Field field) {
|
||||
this.playBoard.setFieldListener(ma, field);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param l Actionlistener
|
||||
*/
|
||||
public final void setSwitchLevelListener(final ActionListener l) {
|
||||
this.switchLevel.addActionListener(l);
|
||||
this.switchLevel.setToolTipText("Choose a difficulty level");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param l Actionlistener
|
||||
*/
|
||||
public final void setNewGameListener(final ActionListener l) {
|
||||
this.newGame.addActionListener(l);
|
||||
this.newGame.setMnemonic(KeyEvent.VK_N);
|
||||
this.newGame.setToolTipText("Start new game");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param l Actionlistener
|
||||
*/
|
||||
public final void setSwitchGameListener(final ActionListener l) {
|
||||
this.switchGame.addActionListener(l);
|
||||
this.switchGame.setMnemonic(KeyEvent.VK_S);
|
||||
this.switchGame.setToolTipText(
|
||||
"Switch first player and start new game");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param l Actionlistener
|
||||
*/
|
||||
public final void setUndoMoveListener(final ActionListener l) {
|
||||
this.undoMove.addActionListener(l);
|
||||
this.undoMove.setMnemonic(KeyEvent.VK_U);
|
||||
this.undoMove.setToolTipText("Undo last move");
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param l Actionlistener
|
||||
*/
|
||||
public final void setQuitGameListener(final ActionListener l) {
|
||||
this.quitGame.addActionListener(l);
|
||||
this.quitGame.setMnemonic(KeyEvent.VK_Q);
|
||||
this.quitGame.setToolTipText("Quit the Game");
|
||||
}
|
||||
}
|
||||
BIN
Tries/Node.class
Normal file
BIN
Tries/Node.class
Normal file
Binary file not shown.
194
Tries/Node.java
Normal file
194
Tries/Node.java
Normal file
@@ -0,0 +1,194 @@
|
||||
/**
|
||||
* Provides methods to manipulate single Nodes.<br />
|
||||
* A Node represents a char in the Trie and provides a children-array
|
||||
* that saves following chars. <br />The Node also saves the parent Node.
|
||||
* if a character is the last one of a string that is set it contains points.
|
||||
* <br /><br />i.e. if "hans" is set with 5 points, <code>points</code>
|
||||
* of 's' is 5.
|
||||
*/
|
||||
public class Node {
|
||||
|
||||
/**
|
||||
* the points of a Student.
|
||||
*/
|
||||
private Integer points;
|
||||
/**
|
||||
* Node array for children.
|
||||
*/
|
||||
private Node[] children = new Node[26];
|
||||
/**
|
||||
* Parent Node.
|
||||
*/
|
||||
private Node parent;
|
||||
/**
|
||||
* The Character of the Node.
|
||||
*/
|
||||
private char ch;
|
||||
|
||||
/**
|
||||
* Constructor for root Node.
|
||||
*/
|
||||
public Node() {
|
||||
this.points = null;
|
||||
this.parent = null;
|
||||
this.ch = '+';
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for Node with character <code>ch</code>.
|
||||
* @param ch character of the Node
|
||||
*/
|
||||
private Node(char ch) {
|
||||
this.points = null;
|
||||
this.parent = null;
|
||||
this.ch = ch;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for Node <code>ch</code>s with known parent
|
||||
* Node <code>parent</code>.
|
||||
* @param ch character of the Node
|
||||
* @param parent Parent Node of the Node
|
||||
*/
|
||||
public Node(char ch, Node parent) {
|
||||
Node hlp = new Node(ch);
|
||||
hlp.parent = parent;
|
||||
parent.setChild(ch, hlp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a character <code>ch</code> into the index for
|
||||
* the children array.
|
||||
* @param ch character to convert
|
||||
* @return index for the children array
|
||||
*/
|
||||
private int convertChar(char ch) {
|
||||
return ch - 'a';
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets <code>child</code> with character <code>ch</code>
|
||||
* in children array.
|
||||
* @param ch the child's character
|
||||
* @param child the child Node
|
||||
*/
|
||||
private void setChild(char ch, Node child) {
|
||||
this.children[convertChar(ch)] = child;
|
||||
}
|
||||
|
||||
/**
|
||||
* Child getter.
|
||||
* @param ch character of child to get
|
||||
* @return <code>Node</code> of child
|
||||
* or <code>null</code>, if child does not exist
|
||||
*/
|
||||
public Node getChild(char ch) {
|
||||
return this.children[convertChar(ch)];
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the subtree for a string <code>key</code>.
|
||||
* @param key string to search for
|
||||
* @return <code>Node</code> of found key or <code>null</code>
|
||||
*/
|
||||
public Node find(String key) {
|
||||
int i = 1;
|
||||
Node iterator = this.children[convertChar(key.charAt(0))];
|
||||
while (iterator != null && i < key.length()) {
|
||||
iterator = iterator.children[convertChar(key.charAt(i))];
|
||||
i++;
|
||||
}
|
||||
if (i == key.length()) {
|
||||
return iterator;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset <code>points</code> of Node.
|
||||
*/
|
||||
public void deleteMe() {
|
||||
this.points = null;
|
||||
cleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for children.
|
||||
* @return <code>true</code>, if child was found or<br />
|
||||
* <code>false</code>, if child not available
|
||||
*/
|
||||
private boolean hasChild() {
|
||||
for (int i = 0; i < this.children.length; i++) {
|
||||
if (this.children[i] != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes unneeded Nodes after Student deletion.
|
||||
*/
|
||||
private void cleanup() {
|
||||
if (hasChild()) {
|
||||
return;
|
||||
}
|
||||
Node iterator = this.parent;
|
||||
char isChild = this.ch;
|
||||
while (iterator.points == null && iterator.parent != null) {
|
||||
|
||||
//delete edge
|
||||
iterator.children[convertChar(isChild)] = null;
|
||||
|
||||
//if another child is set, the Node is still needed
|
||||
if (iterator.hasChild()) {
|
||||
return;
|
||||
}
|
||||
isChild = iterator.ch;
|
||||
iterator = iterator.parent;
|
||||
}
|
||||
|
||||
//delete child Node in root
|
||||
iterator.children[convertChar(isChild)] = null;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to print a Node and his subtree.<br />
|
||||
* Recursive self-calling every child of the Node.
|
||||
* @return <code>string</code> of Node and subtree
|
||||
*/
|
||||
public String toString() {
|
||||
System.out.print(this.ch);
|
||||
if (this.points != null) {
|
||||
System.out.print("[" + this.points + "]");
|
||||
}
|
||||
if (this.hasChild()) {
|
||||
System.out.print("(");
|
||||
for (int i = 0; i < this.children.length; i++) {
|
||||
if (this.children[i] != null) {
|
||||
this.children[i].toString();
|
||||
}
|
||||
}
|
||||
System.out.print(")");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for points of Node.
|
||||
* @param points to set
|
||||
*/
|
||||
public void setPoints(Integer points) {
|
||||
this.points = points;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for Points of Node.
|
||||
* @return <code>points</code> of Node
|
||||
*/
|
||||
public Integer getPoints() {
|
||||
return this.points;
|
||||
}
|
||||
}
|
||||
BIN
Tries/Shell.class
Normal file
BIN
Tries/Shell.class
Normal file
Binary file not shown.
170
Tries/Shell.java
Normal file
170
Tries/Shell.java
Normal file
@@ -0,0 +1,170 @@
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Main Class for the Student-Administration.<br />
|
||||
* The user types commands and parameters to manipulate or change
|
||||
* the Trie. <br />Commands are found with the HELP command
|
||||
*/
|
||||
final class Shell {
|
||||
private Shell() { }
|
||||
/**
|
||||
* Checks if <code>string</code> consists of only chars.
|
||||
* @param string String to check
|
||||
* @return <code>true</code>, if <code>string</code> has only chars<br />
|
||||
* <code>false</code>, if <code>string</code> has other signs
|
||||
*/
|
||||
private static boolean isCharString(String string) {
|
||||
for (int j = 0; j < string.length(); j++) {
|
||||
if (string.charAt(j) - 'a' < 0
|
||||
|| string.charAt(j) - 'a' > 25) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main method for the Student Administration including the Shell.
|
||||
* Validation for input.
|
||||
* @param args are not handled at the moment
|
||||
* @throws IOException
|
||||
*/
|
||||
public static void main(String[] args) throws IOException {
|
||||
Trie t = new Trie();
|
||||
boolean runtime = true;
|
||||
BufferedReader br = new BufferedReader(
|
||||
new InputStreamReader(System.in));
|
||||
while (runtime) {
|
||||
System.out.print("trie> ");
|
||||
String input = br.readLine();
|
||||
String[] splited = input.split(" ");
|
||||
char ch = splited[0].charAt(0);
|
||||
ch = Character.toLowerCase(ch);
|
||||
try {
|
||||
switch(ch) {
|
||||
case 'n' :
|
||||
if (splited.length != 1) {
|
||||
System.out.println("Error! Wrong Parameter! "
|
||||
+ "check HELP");
|
||||
break;
|
||||
}
|
||||
t = new Trie();
|
||||
break;
|
||||
case 'a' :
|
||||
if (splited.length != 3) {
|
||||
System.out.println("Error! Wrong Parameter! "
|
||||
+ "check HELP");
|
||||
break;
|
||||
}
|
||||
if (!isCharString(splited[1])) {
|
||||
System.out.println("Error! Wrong Parameter! "
|
||||
+ "check HELP");
|
||||
break;
|
||||
}
|
||||
if (Integer.parseInt(splited[2]) < 0) {
|
||||
System.out.println("Error! Wrong Parameter! "
|
||||
+ "check HELP");
|
||||
break;
|
||||
}
|
||||
if (!t.add(splited[1], Integer.parseInt(splited[2]))) {
|
||||
System.out.println("Error! " + splited[1]
|
||||
+ " is already present.");
|
||||
}
|
||||
break;
|
||||
case 'c' :
|
||||
if (splited.length != 3) {
|
||||
System.out.println("Error! Wrong Parameter! "
|
||||
+ "check HELP");
|
||||
break;
|
||||
}
|
||||
if (!isCharString(splited[1])) {
|
||||
System.out.println("Error! Wrong Parameter! "
|
||||
+ "check HELP");
|
||||
break;
|
||||
}
|
||||
if (Integer.parseInt(splited[2]) < 0) {
|
||||
System.out.println("Error! Wrong Parameter! "
|
||||
+ "check HELP");
|
||||
break;
|
||||
}
|
||||
if (!t.change(splited[1], Integer.parseInt(splited[2]))) {
|
||||
System.out.println("Error! " + splited[1]
|
||||
+ " does not yet exist");
|
||||
}
|
||||
break;
|
||||
case 'd' :
|
||||
if (splited.length != 2) {
|
||||
System.out.println("Error! Wrong Parameter! "
|
||||
+ "check HELP");
|
||||
break;
|
||||
}
|
||||
if (!isCharString(splited[1])) {
|
||||
System.out.println("Error! Wrong Parameter! "
|
||||
+ "check HELP");
|
||||
break;
|
||||
}
|
||||
if (!t.delete(splited[1])) {
|
||||
System.out.println("Error! " + splited[1]
|
||||
+ " does not yet exist");
|
||||
}
|
||||
break;
|
||||
case 'p' :
|
||||
if (splited.length != 2) {
|
||||
System.out.println("Error! Wrong Parameter! "
|
||||
+ "check HELP");
|
||||
break;
|
||||
}
|
||||
if (!isCharString(splited[1])) {
|
||||
System.out.println("Error! Wrong Parameter! "
|
||||
+ "check HELP");
|
||||
break;
|
||||
}
|
||||
if (t.points(splited[1]) != null) {
|
||||
System.out.println(t.points(splited[1]));
|
||||
} else {
|
||||
System.out.println("Error! " + splited[1]
|
||||
+ " does not yet exist");
|
||||
}
|
||||
|
||||
break;
|
||||
case 't' :
|
||||
if (splited.length != 1) {
|
||||
System.out.println("Error! Wrong Parameter! "
|
||||
+ "check HELP");
|
||||
break;
|
||||
}
|
||||
System.out.println(t.toString());
|
||||
break;
|
||||
case 'h' :
|
||||
if (splited.length != 1) {
|
||||
System.out.println("Error! Wrong Parameter! "
|
||||
+ "check HELP");
|
||||
break;
|
||||
}
|
||||
System.out.println("NEW Creates a new empty Trie ");
|
||||
System.out.println("ADD adds <name> with <points>");
|
||||
System.out.println("CHANGE change <points> of <name>");
|
||||
System.out.println("DELETE deletes <name>");
|
||||
System.out.println("POINTS displays points of <name>");
|
||||
System.out.println("TRIE displays hole Trie");
|
||||
System.out.println("HELP display this help");
|
||||
System.out.println("QUIT exit Shell");
|
||||
break;
|
||||
case 'q' :
|
||||
if (splited.length != 1) {
|
||||
System.out.println("Error! Wrong Parameter! check HELP");
|
||||
break;
|
||||
}
|
||||
runtime = false;
|
||||
break;
|
||||
default :
|
||||
System.out.println("Error! Wrong command check HELP");
|
||||
}
|
||||
} catch (NumberFormatException e) {
|
||||
System.out.println("Error! Wrong Parameter! check HELP");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
Tries/Trie.class
Normal file
BIN
Tries/Trie.class
Normal file
Binary file not shown.
103
Tries/Trie.java
Normal file
103
Tries/Trie.java
Normal file
@@ -0,0 +1,103 @@
|
||||
/**
|
||||
* Provides methods to manipulate the Trie. <br />
|
||||
* The Trie only saves the root Node of which every Trie-Operation starts.
|
||||
*/
|
||||
public class Trie {
|
||||
|
||||
/**
|
||||
* The root Node of the Trie.
|
||||
*/
|
||||
private Node root;
|
||||
/**
|
||||
* creates new Trie.
|
||||
*/
|
||||
public Trie() {
|
||||
this.root = new Node();
|
||||
}
|
||||
|
||||
/**
|
||||
* adds new Student <code>key</code> with
|
||||
* points <code>points</code> to the Trie.
|
||||
* @param key Student to add
|
||||
* @param points points of Student
|
||||
* @return <code>true</code> if add was successful or <br />
|
||||
* <code>false</code> if Student already exists
|
||||
*/
|
||||
public boolean add(String key, Integer points) {
|
||||
Node iterator = this.root;
|
||||
for (int i = 0; i < key.length(); i++) {
|
||||
if (iterator.getChild(key.charAt(i)) == null) {
|
||||
new Node(key.charAt(i), iterator);
|
||||
}
|
||||
iterator = iterator.getChild(key.charAt(i));
|
||||
}
|
||||
if (iterator.getPoints() == null) {
|
||||
iterator.setPoints(points);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* deletes Student <code>key</code> in the Trie.
|
||||
* @param key Student to delete
|
||||
* @return <code>true</code> if delete was successful or <br />
|
||||
* <code>false></code> if Student does not exist
|
||||
*/
|
||||
public boolean delete(String key) {
|
||||
Node hlp = this.root.find(key);
|
||||
if (hlp == null) {
|
||||
return false;
|
||||
}
|
||||
if (hlp.getPoints() != null) {
|
||||
hlp.deleteMe();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* changes points <code>points</code> of Student <code>key</code>
|
||||
* in the Trie.
|
||||
* @param key Student to change
|
||||
* @param points points of Student to change
|
||||
* @return <code>true</code> if change was successful or<br />
|
||||
* <code>false</code> if Student does not exist
|
||||
*/
|
||||
public boolean change(String key, Integer points) {
|
||||
Node hlp = this.root.find(key);
|
||||
if (hlp == null) {
|
||||
return false;
|
||||
}
|
||||
if (hlp.getPoints() != null) {
|
||||
hlp.setPoints(points);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for points of Student <code>key</code> in the Trie.
|
||||
* @param key Student to search for
|
||||
* @return <code>points</code> of Student or<br />
|
||||
* <code>null</code> if Student does not exist
|
||||
*/
|
||||
public Integer points(String key) {
|
||||
Node hlp = this.root.find(key);
|
||||
if (hlp != null) {
|
||||
return hlp.getPoints();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to print the hole Trie.
|
||||
* @return <code>string</code> of the Trie in the requested format
|
||||
*/
|
||||
public String toString() {
|
||||
return this.root.toString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user