Last active
May 30, 2019 10:41
-
-
Save Glorfindel83/d5a9f1771671207437988a798547a536 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package net.mathoverflow; | |
import java.awt.Point; | |
// See https://mathoverflow.net/a/332788/70594 | |
public class KnightsTour3Picture { | |
public static void main(String[] args) throws Exception { | |
System.out.println("<!DOCTYPE html>"); | |
System.out.println("<html>"); | |
System.out.println("<body>"); | |
System.out.println("<svg height=\"" + center.y * 2 + "\" width=\"" + center.x * 2 + "\">"); | |
// Grid | |
int start = UNIT / 2, end = UNIT * HALF_SIZE * 2 - UNIT / 2; | |
for (int i = start; i <= end; i += UNIT) { | |
String color = ((UNIT * HALF_SIZE - i) * 2 / UNIT) % 3 == 0 ? "#BBB" : "#DDD"; | |
System.out.println("\t<line stroke=\"" + color + "\" x1=\"" + start + "\" y1=\"" + i + "\" x2=\"" + end | |
+ "\" y2=\"" + i + "\" />"); | |
System.out.println("\t<line stroke=\"" + color + "\" x1=\"" + i + "\" y1=\"" + start + "\" x2=\"" + i | |
+ "\" y2=\"" + end + "\" />"); | |
} | |
// Missing points | |
for (int i = UNIT * HALF_SIZE - 3 * UNIT * (HALF_SIZE / 3); i < end; i += UNIT * 3) { | |
if (i == 0) | |
continue; | |
for (int j = UNIT * HALF_SIZE - 3 * UNIT * (HALF_SIZE / 3); j < end; j += UNIT * 3) { | |
if (j == 0) | |
continue; | |
System.out | |
.println("\t<circle fill=\"gray\" cx=\"" + i + "\" cy=\"" + j + "\" r=\"" + UNIT / 8 + "\" />"); | |
} | |
} | |
// Winding | |
int wx = center.x + UNIT * 3 / 2, wy = center.y + UNIT * 3 / 2; | |
StringBuilder windings = new StringBuilder( | |
"\t<path stroke=\"black\" fill=\"none\" d=\"M" + wx + " " + wy + " "); | |
wx -= UNIT * 3; | |
windings.append("L" + wx + " " + wy + " "); | |
wy -= UNIT * 3; | |
windings.append("L" + wx + " " + wy + " "); | |
// Tour | |
for (int winding = 0; winding <= MAX_WINDINGS; winding++) { | |
for (Direction direction_ : Direction.values()) { | |
direction = direction_; | |
switch (direction) { | |
case RIGHT: | |
wx += UNIT * 3 * (2 + winding * 2); | |
if (winding == MAX_WINDINGS) { | |
wx -= UNIT * 3; | |
} | |
windings.append("L" + wx + " " + wy + " "); | |
for (int i = 0; i < winding; i++) { | |
new Box(i == 0 ? 0x9 : 0xC); | |
new Box(0xE); | |
} | |
new Box(0xC); | |
break; | |
case DOWN: | |
wy += UNIT * 3 * (2 + winding * 2); | |
windings.append("L" + wx + " " + wy + " "); | |
for (int i = 0; i < winding; i++) { | |
new Box(0xF); | |
new Box(0x1); | |
} | |
new Box(0xF); | |
break; | |
case LEFT: | |
wx -= UNIT * 3 * (3 + winding * 2); | |
windings.append("L" + wx + " " + wy + " "); | |
new Box(0x1); | |
new Box(0x6); | |
for (int i = 0; i < winding; i++) { | |
new Box(0x5); | |
new Box(0x3); | |
} | |
break; | |
case UP: | |
wy -= UNIT * 3 * (3 + winding * 2); | |
windings.append("L" + wx + " " + wy + " "); | |
new Box(0x5); | |
new Box(0xA); | |
for (int i = 0; i < winding; i++) { | |
new Box(0x9); | |
new Box(0x7); | |
} | |
break; | |
} | |
if (winding == MAX_WINDINGS) | |
break; | |
} | |
} | |
windings.append("\" />"); | |
System.out.println("\" />"); | |
System.out.println(points); | |
System.out.println(windings); | |
System.out.println("</svg>"); | |
System.out.println("</body>"); | |
System.out.println("</html>"); | |
} | |
private static final int UNIT = 30, HALF_SIZE = 12, MAX_WINDINGS = 3; | |
private static final Point center = new Point(UNIT * HALF_SIZE, UNIT * HALF_SIZE); | |
private static boolean first = true; | |
private static final StringBuilder points = new StringBuilder(); | |
// Center position of current box | |
private static int cx = 0, cy = 0; | |
private static Direction direction; | |
// Cf. https://i.stack.imgur.com/WGZ9R.png | |
private static class Box { | |
// This is the path the knight will take through the box (direction and starting position depend on the box id). | |
private static final int[] X = { -1, 0, 1, -1, 1, 0, -1, 1 }, Y = { -1, 1, -1, 0, 1, -1, 1, 0 }; | |
// Boxes 0 and 1 start at X[0], Y[0]; boxes 2 and 3 at X[5], Y[5], etc. | |
private static final int[] START = { 0, 5, 2, 7, 4, 1, 6, 3 }; | |
private static final String[] COLORS = { "black", "#F80", "black", "#FF0", "black", "#880", "#F0F", "#0F0", | |
"black", "#080", "#808", "black", "#0FF", "black", "#08F", "#F00" }; | |
// The box id is a hexadecimal digit | |
private Box(int id) { | |
boolean forward = id % 2 == 0; | |
int start = START[id / 2], x = 0, y = 0; | |
for (int i = 0; i < 8; i++) { | |
x = center.x + UNIT * (cx + X[(start + (forward ? i : (8 - i))) % 8]); | |
y = center.y + UNIT * (cy + Y[(start + (forward ? i : (8 - i))) % 8]); | |
points.append("\n\t<circle fill=\"" + COLORS[id] + "\" cx=\"" + x + "\" cy=\"" + y + "\" r=\"" | |
+ UNIT / 8 + "\" />"); | |
if (i == 0) { | |
if (first) { | |
System.out.println("\t<circle fill=\"" + COLORS[id] + "\" cx=\"" + x + "\" cy=\"" + y | |
+ "\" r=\"" + UNIT / 3 + "\" />"); | |
first = false; | |
} else { | |
// Close transition path | |
System.out.print("L" + x + " " + y + " "); | |
System.out.println("\" />"); | |
} | |
// Own path | |
System.out.print("\t<path stroke=\"" + COLORS[id] + "\" fill=\"none\" d=\"M"); | |
} else { | |
System.out.print("L"); | |
} | |
System.out.print(x + " " + y + " "); | |
} | |
System.out.println("\" />"); | |
System.out.print("\t<path stroke=\"grey\" fill=\"none\" d=\"M" + x + " " + y + " "); | |
switch (direction) { | |
case RIGHT: | |
cx += 3; | |
break; | |
case DOWN: | |
cy += 3; | |
break; | |
case LEFT: | |
cx -= 3; | |
break; | |
case UP: | |
cy -= 3; | |
break; | |
} | |
} | |
} | |
private enum Direction { | |
RIGHT, DOWN, LEFT, UP | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment