initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.idea
|
||||
24
pom.xml
Normal file
24
pom.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>nl.spijkerman.ivo</groupId>
|
||||
<artifactId>binaire</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>22</maven.compiler.source>
|
||||
<maven.compiler.target>22</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>RELEASE</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
138
src/main/java/com/spijkerman/ivo/binaire/Game.java
Normal file
138
src/main/java/com/spijkerman/ivo/binaire/Game.java
Normal file
@@ -0,0 +1,138 @@
|
||||
package com.spijkerman.ivo.binaire;
|
||||
|
||||
|
||||
import com.spijkerman.ivo.binaire.rules.Rules;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class Game {
|
||||
|
||||
private final JFrame keyFrame;
|
||||
private final Value[][] board;
|
||||
private int cursorX, cursorY;
|
||||
|
||||
|
||||
public static void main(String[] args) {
|
||||
var scanner = new Scanner(System.in);
|
||||
System.out.println("Enter width: ");
|
||||
var width = scanner.nextInt();
|
||||
System.out.println("Enter height: ");
|
||||
var height = scanner.nextInt();
|
||||
new Game(width, height);
|
||||
}
|
||||
|
||||
public Game(int width, int height) {
|
||||
try {
|
||||
this.board = new Value[width][height];
|
||||
this.keyFrame = createJFrame();
|
||||
var rules = new Rules();
|
||||
while (!isDone()) {
|
||||
draw();
|
||||
prompt();
|
||||
if (rules.applyUntilDone(board)) {
|
||||
moveNextEmpty();
|
||||
}
|
||||
}
|
||||
cursorY = -1; // Move out of sight to show entire puzzle
|
||||
System.out.println("Congratulations!");
|
||||
draw();
|
||||
} catch (Exception e) {
|
||||
cursorY = -1;
|
||||
draw();
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
throw new RuntimeException("Unreachable code");
|
||||
}
|
||||
}
|
||||
|
||||
private void draw() {
|
||||
var borderLine = "+" + "-".repeat(board.length) + "+\n";
|
||||
var field = new StringBuilder(borderLine);
|
||||
for (var y = 0; y < board[0].length; y++) {
|
||||
field.append('|');
|
||||
for (var x = 0; x < board.length; x++) {
|
||||
if (x == cursorX && y == cursorY) {
|
||||
field.append('_');
|
||||
} else if (board[x][y] == null) {
|
||||
field.append(' ');
|
||||
} else {
|
||||
field.append(switch (board[x][y]) {
|
||||
case ONE_USER -> '1';
|
||||
case ONE_SYSTEM -> 'I';
|
||||
case ZERO_USER -> '0';
|
||||
case ZERO_SYSTEM -> 'O';
|
||||
});
|
||||
}
|
||||
}
|
||||
field.append("|\n");
|
||||
}
|
||||
field.append(borderLine);
|
||||
System.out.println(field);
|
||||
}
|
||||
|
||||
private void prompt() {
|
||||
System.out.printf("Press arrow key, 0, 1, q or c (%d,%d):%n", cursorX + 1, cursorY + 1);
|
||||
var eventHolder = new CompletableFuture<KeyEvent>();
|
||||
var keyAdapter = new KeyAdapter() {
|
||||
@Override
|
||||
public void keyPressed(KeyEvent e) {
|
||||
super.keyPressed(e);
|
||||
eventHolder.complete(e);
|
||||
}
|
||||
};
|
||||
keyFrame.addKeyListener(keyAdapter);
|
||||
switch (eventHolder.join().getKeyCode()) {
|
||||
case 37 -> cursorX = Math.max(0, cursorX - 1);
|
||||
case 38 -> cursorY = Math.max(0, cursorY - 1);
|
||||
case 39 -> cursorX = Math.min(board.length - 1, cursorX + 1);
|
||||
case 40 -> cursorY = Math.min(board[0].length - 1, cursorY + 1);
|
||||
case 48 -> moveNextEmpty(board[cursorX][cursorY] = Value.ZERO_USER);
|
||||
case 49 -> moveNextEmpty(board[cursorX][cursorY] = Value.ONE_USER);
|
||||
case 67 -> moveNextEmpty(board[cursorX][cursorY] = null);
|
||||
case 81 -> System.exit(1);
|
||||
}
|
||||
keyFrame.removeKeyListener(keyAdapter);
|
||||
}
|
||||
|
||||
private static JFrame createJFrame() {
|
||||
var frame = new JFrame();
|
||||
frame.setUndecorated(true);
|
||||
frame.setSize(1, 1);
|
||||
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
frame.setOpacity(0);
|
||||
frame.setLocation(-100, -100);
|
||||
frame.setFocusable(true);
|
||||
frame.setVisible(true);
|
||||
return frame;
|
||||
}
|
||||
|
||||
private void moveNextEmpty(Value... ignore) {
|
||||
if (isDone()) {
|
||||
return;
|
||||
}
|
||||
while (board[cursorX][cursorY] != null) {
|
||||
if (++cursorX >= board.length) {
|
||||
cursorX = 0;
|
||||
if (++cursorY >= board[0].length) {
|
||||
cursorY = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDone() {
|
||||
for (var row : board) {
|
||||
for (var cell : row) {
|
||||
if (cell == null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
15
src/main/java/com/spijkerman/ivo/binaire/Rule.java
Normal file
15
src/main/java/com/spijkerman/ivo/binaire/Rule.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package com.spijkerman.ivo.binaire;
|
||||
|
||||
public interface Rule {
|
||||
|
||||
default boolean applyUntilDone(Value[][] board) {
|
||||
var changed = false;
|
||||
while (apply(board)) {
|
||||
changed = true;
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
boolean apply(Value[][] board);
|
||||
|
||||
}
|
||||
29
src/main/java/com/spijkerman/ivo/binaire/Value.java
Normal file
29
src/main/java/com/spijkerman/ivo/binaire/Value.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package com.spijkerman.ivo.binaire;
|
||||
|
||||
public enum Value {
|
||||
ONE_USER(1),
|
||||
ONE_SYSTEM(1),
|
||||
ZERO_USER(0),
|
||||
ZERO_SYSTEM(0);
|
||||
|
||||
public final int val;
|
||||
|
||||
Value(int val) {
|
||||
this.val = val;
|
||||
}
|
||||
|
||||
public boolean isSame(Value that) {
|
||||
if (that == null) {
|
||||
return false;
|
||||
}
|
||||
return this.val == that.val;
|
||||
}
|
||||
|
||||
public Value system() {
|
||||
return val == 0 ? ZERO_SYSTEM : ONE_SYSTEM;
|
||||
}
|
||||
|
||||
public Value reverseSystem() {
|
||||
return val == 0 ? ONE_SYSTEM : ZERO_SYSTEM;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.spijkerman.ivo.binaire.rules;
|
||||
|
||||
import com.spijkerman.ivo.binaire.Value;
|
||||
|
||||
public class DoubleRule extends StraightRule {
|
||||
@Override
|
||||
Value[] apply(Value[] line) {
|
||||
for (int i = 0; i < line.length - 1; i++) {
|
||||
var curr = line[i];
|
||||
if (curr == null) {
|
||||
continue;
|
||||
}
|
||||
var same = curr.isSame(line[i + 1]);
|
||||
if (!same) {
|
||||
continue;
|
||||
}
|
||||
var before = i - 1;
|
||||
if (before >= 0 && line[before] == null) {
|
||||
line[before] = curr.reverseSystem();
|
||||
return line;
|
||||
}
|
||||
var after = i + 2;
|
||||
if (after < line.length && line[after] == null) {
|
||||
line[after] = curr.reverseSystem();
|
||||
return line;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.spijkerman.ivo.binaire.rules;
|
||||
|
||||
import com.spijkerman.ivo.binaire.Value;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class EnsureEqualValues extends StraightRule {
|
||||
@Override
|
||||
Value[] apply(Value[] line) {
|
||||
if (line.length % 2 != 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
var half = line.length / 2;
|
||||
var zeroes = 0;
|
||||
var ones = 0;
|
||||
for (var cell : line) {
|
||||
if (cell == null) {
|
||||
continue;
|
||||
} else if (cell.val == 0 && (++zeroes > half)) {
|
||||
throw new IllegalStateException(Arrays.toString(line) + " has too many zeroes");
|
||||
} else if (cell.val == 1 && (++ones > half)){
|
||||
throw new IllegalStateException(Arrays.toString(line) + " has too many ones");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.spijkerman.ivo.binaire.rules;
|
||||
|
||||
import com.spijkerman.ivo.binaire.Value;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class EnsureNoTriplets extends StraightRule {
|
||||
@Override
|
||||
Value[] apply(Value[] line) {
|
||||
for (int i = 2; i < line.length; i++) {
|
||||
var left = line[i-2];
|
||||
if (left == null) {
|
||||
continue;
|
||||
}
|
||||
var middle = line[i-1];
|
||||
if (!left.isSame(middle)) {
|
||||
continue;
|
||||
}
|
||||
var right = line[i];
|
||||
if (left.isSame(right)) {
|
||||
throw new IllegalStateException(Arrays.toString(line) + " contains a triplet of " + left.val);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
43
src/main/java/com/spijkerman/ivo/binaire/rules/HalfRule.java
Normal file
43
src/main/java/com/spijkerman/ivo/binaire/rules/HalfRule.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package com.spijkerman.ivo.binaire.rules;
|
||||
|
||||
import com.spijkerman.ivo.binaire.Value;
|
||||
|
||||
public class HalfRule extends StraightRule {
|
||||
|
||||
@Override
|
||||
Value[] apply(Value[] line) {
|
||||
if (line.length % 2 != 0) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
var half = line.length / 2;
|
||||
var ones = 0;
|
||||
var zeroes = 0;
|
||||
for (var cell : line) {
|
||||
if (cell == null) {
|
||||
continue;
|
||||
} else if (cell.val == 0) {
|
||||
zeroes++;
|
||||
} else {
|
||||
ones++;
|
||||
}
|
||||
}
|
||||
if (zeroes == ones) {
|
||||
return null;
|
||||
}
|
||||
final Value toFill;
|
||||
if (ones == half) {
|
||||
toFill = Value.ZERO_SYSTEM;
|
||||
} else if (zeroes == half) {
|
||||
toFill = Value.ONE_SYSTEM;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
for (var i = 0; i < line.length; i++) {
|
||||
if (line[i] == null) {
|
||||
line[i] = toFill;
|
||||
}
|
||||
}
|
||||
System.err.println("Applied half rule!");
|
||||
return line;
|
||||
}
|
||||
}
|
||||
24
src/main/java/com/spijkerman/ivo/binaire/rules/Rules.java
Normal file
24
src/main/java/com/spijkerman/ivo/binaire/rules/Rules.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package com.spijkerman.ivo.binaire.rules;
|
||||
|
||||
import com.spijkerman.ivo.binaire.Rule;
|
||||
import com.spijkerman.ivo.binaire.Value;
|
||||
|
||||
public class Rules implements Rule {
|
||||
|
||||
private static final Rule[] rules = {
|
||||
new DoubleRule(),
|
||||
new TripleRule(),
|
||||
new HalfRule(),
|
||||
new EnsureEqualValues(),
|
||||
new EnsureNoTriplets()
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean apply(Value[][] board) {
|
||||
var changed = false;
|
||||
for (var rule : rules) {
|
||||
changed |= rule.apply(board);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.spijkerman.ivo.binaire.rules;
|
||||
|
||||
import com.spijkerman.ivo.binaire.Rule;
|
||||
import com.spijkerman.ivo.binaire.Value;
|
||||
|
||||
public abstract class StraightRule implements Rule {
|
||||
|
||||
@Override
|
||||
public boolean apply(Value[][] board) {
|
||||
var width = board.length;
|
||||
var height = board[0].length;
|
||||
var max = Math.max(width, height);
|
||||
|
||||
for (var i = 0; i < max; i++) {
|
||||
if (i < width) {
|
||||
var column = new Value[height];
|
||||
System.arraycopy(board[i], 0, column, 0, height);
|
||||
column = apply(column);
|
||||
if (column != null) {
|
||||
System.arraycopy(column, 0, board[i], 0, height);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (i < height) {
|
||||
var row = new Value[width];
|
||||
for (int j = 0; j < width; j++) {
|
||||
row[j] = board[j][i];
|
||||
}
|
||||
row = apply(row);
|
||||
if (row != null) {
|
||||
for (int j = 0; j < width; j++) {
|
||||
board[j][i] = row[j];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
abstract Value[] apply(Value[] line);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.spijkerman.ivo.binaire.rules;
|
||||
|
||||
import com.spijkerman.ivo.binaire.Value;
|
||||
|
||||
public class TripleRule extends StraightRule {
|
||||
@Override
|
||||
Value[] apply(Value[] line) {
|
||||
for (int i = 0; i < line.length - 2; i ++) {
|
||||
var left = line[i];
|
||||
if (left == null) {
|
||||
continue;
|
||||
}
|
||||
var same = left.isSame(line[i + 2]);
|
||||
if (!same) {
|
||||
continue;
|
||||
}
|
||||
var middle = i + 1;
|
||||
if (line[middle] == null) {
|
||||
line[middle] = left.reverseSystem();
|
||||
return line;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user