xxxxxxxxxx
527
let n = 12;
const c_City = 'yellow'; // c_ = color
const c_Number = 'orange';
const c_MarkedCity = 'red';
const c_RoundTest = 'cyan';
const c_RoundBest = 'darkblue';
const c_LinearTest = 'lightgreen';
const c_LinearBest = 'darkgreen';
const c_FarestTest = 'pink';
const c_FarestBest = 'darkmagenta';
const c_Back = 'black';
const c_Info = '#444444';
const c_High = 'beige';
const w_Test = 1; // w_ = weight
const w_Best = 10;
const infoSide = 140;
const R = 20; // Radius of City
const TS = 25; // Text Size
const MAR = 5; // Margin
const SPACE = 32;
const INSERT = 45;
const HOME = 36;
const END = 35;
let TL1, TL2, TL3, TL4, TL5; // Top Lines (numbering down)
let BL1, BL2, BL3, BL4, BL5, BL6, BL7; // Bottom Lines (numbering up)
let city = [], d = [], markedCity = [];
let RoundTest = [], LinearTest = [], FarestTest = [];
let RoundTestTrack, LinearTestTrack, FarestTestTrack;
let RoundBest = [], LinearBest = [], FarestBest = [];
let RoundBestTrack, LinearBestTrack, FarestBestTrack;
let RoundClosing, LinearClosing, FarestClosing;
let mode = 'ROUND', method = 'SWAP', numb = false, best = true, test = true, lang = 'EN';
let t_City, t_Swap, t_Chain, t_Undo, t_Round, t_Linear, t_Farest, t_New, t_Again, t_Copy, t_Number, t_Best, t_Test; // t_ = text
let swapCity1, swapCity2, lastSwapCity1, lastSwapCity2;
this.focus();
function setTitles() {
if (lang == 'EN') {
t_City = 'CITY';
t_Swap = 'SWAP';
t_Chain = 'CHAIN';
t_Undo = 'UNDO';
t_Round = 'ROUND';
t_Linear = 'LINEAR';
t_Farest = 'FAREST';
t_New = 'NEW';
t_Again = 'AGAIN';
t_Copy = 'COPY';
t_Number = 'NUMBER';
t_Best = 'BEST';
t_Test = 'TEST';
}
if (lang == 'HU') {
t_City = 'VÁROS';
t_Swap = 'CSERE';
t_Chain = 'LÁNC';
t_Undo = 'VISSZA';
t_Round = 'KÖRÚT';
t_Linear = 'VONAL';
t_Farest = 'TÁVOLI';
t_New = 'ÚJ';
t_Again = 'MEGINT';
t_Copy = 'MÁSOL';
t_Number = 'SZÁMOK';
t_Best = 'LEGJOBB';
t_Test = 'PRÓBA';
}
}
function setup() {
createCanvas(windowWidth, windowHeight);
ellipseMode(CENTER);
textStyle(BOLD);
textAlign(CENTER, TOP);
textSize(TS);
TL1 = 2 * TS + MAR;
TL2 = TL1 + 2 * TS + MAR;
TL3 = TL2 + 3 * TS + MAR;
TL4 = TL3 + 3 * TS + MAR;
TL5 = TL4 + 3 * TS + MAR;
BL1 = height - TS - MAR;
BL2 = BL1 - TS - MAR;
BL3 = BL2 - TS - MAR;
BL4 = BL3 - TS - MAR;
BL5 = BL4 - TS - MAR;
BL6 = BL5 - TS - MAR;
BL7 = BL6 - TS - MAR;
setTitles();
startGame();
}
function startGame() {
createEntity();
setFarestCity();
resetTracks();
clearSwapCity();
}
function resetTracks() {
RoundTestTrack = trackLength(RoundTest, 'ROUND');
RoundBestTrack = RoundTestTrack;
LinearTestTrack = trackLength(LinearTest, 'LINEAR');
LinearBestTrack = LinearTestTrack;
FarestTestTrack = trackLength(FarestTest, 'FAREST');
FarestBestTrack = FarestTestTrack;
}
function distance() {
for (let i = 0; i < n; i++) {
d[i] = [];
for (let j = 0; j < n; j++) {
d[i][j] = dist(city[i].x, city[i].y, city[j].x, city[j].y);
}
}
}
function setFarestCity() {
let ac, t, di, dimax = 0;
for (let i = 1; i < n; i++) {
di = dist(city[0].x, city[0].y, city[i].x, city[i].y);
if (di > dimax) {dimax = di; ac = i;}
}
t = city[n - 1]; city[n - 1] = city[ac]; city[ac] = t;
distance();
FarestClosing = d[0][n - 1];
}
function createEntity() {
for (let i = 0; i < n; i++) {
city[i] = createVector(random(infoSide + R, width - R),
random(R, height - R));
RoundTest[i] = i;
RoundBest[i] = i;
LinearTest[i] = i;
LinearBest[i] = i;
FarestTest[i] = i;
FarestBest[i] = i;
markedCity[i] = 0;
}
}
function drawTrack(track, color, weight) {
stroke(color);
strokeWeight(weight);
beginShape();
for (let i = 0; i < n; i++) {
vertex(city[track[i]].x, city[track[i]].y);
}
if (mode == 'ROUND') { vertex(city[0].x, city[0].y) }
endShape();
}
function drawCity() {
noStroke();
// City
for (let i = 1; i < n; i++) {
if (markedCity[i] == 1) { fill(c_MarkedCity) } else { fill(c_City) }
circle(city[i].x, city[i].y, R);
}
// City #0
fill(c_City);
circle(city[0].x, city[0].y, 1.4 * R);
fill(c_Back);
circle(city[0].x, city[0].y, 0.7 * R);
// Number
if (numb) {
fill(c_Number);
for (let i = 1; i < n; i++) {
text(i, city[i].x + 25, city[i].y);
}
}
}
function drawInfo() {
stroke(c_Info);
strokeWeight(1);
line(infoSide, 0, infoSide, height);
line(0, TL1, infoSide, TL1);
line(0, TL2, infoSide, TL2);
line(0, TL3, infoSide, TL3);
line(0, TL4, infoSide, TL4);
line(0, TL5, infoSide, TL5);
line(0, BL1, infoSide, BL1);
line(0, BL2, infoSide, BL2);
line(0, BL3, infoSide, BL3);
line(0, BL4, infoSide, BL4);
line(0, BL5, infoSide, BL5);
line(0, BL6, infoSide, BL6);
line(0, BL7, infoSide, BL7);
noStroke();
fill(c_City);
text(t_City, infoSide / 2, MAR);
text(n, infoSide / 2, MAR + TS);
fill(c_High);
if (method == 'SWAP') {
text(t_Swap, infoSide / 2, TL1 + MAR)
} else {
text(t_Chain, infoSide / 2, TL1 + MAR)
}
fill(c_MarkedCity);
text('-', infoSide / 2, TL1 + MAR + TS);
if (mode == 'ROUND') { fill(c_RoundTest) } else { fill(c_Info) }
text(t_Round, infoSide / 2, TL2 + MAR);
text(int(RoundBestTrack), infoSide / 2, TL2 + MAR + TS);
if (RoundTestTrack > RoundBestTrack) {
text('( ' + int(RoundTestTrack) + ' )', infoSide / 2, TL2 + MAR + 2 * TS);
}
if (mode == 'LINEAR') { fill(c_LinearTest) } else { fill(c_Info) }
text(t_Linear, infoSide / 2, TL3 + MAR);
text(int(LinearBestTrack), infoSide / 2, TL3 + MAR + TS);
if (LinearTestTrack > LinearBestTrack) {
text('( ' + int(LinearTestTrack) + ' )', infoSide / 2, TL3 + MAR + 2 * TS);
}
if (mode == 'FAREST') { fill(c_FarestTest) } else { fill(c_Info) }
text(t_Farest, infoSide / 2, TL4 + MAR);
text(int(FarestBestTrack), infoSide / 2, TL4 + MAR + TS);
if (FarestTestTrack > FarestBestTrack) {
text('( ' + int(FarestTestTrack) + ' )', infoSide / 2, TL4 + MAR + 2 * TS);
}
fill(c_High);
text(t_New, infoSide / 2, BL7 + MAR);
text(t_Again, infoSide / 2, BL6 + MAR);
text(t_Copy, infoSide / 2, BL5 + MAR);
if (lastSwapCity1 != 0) { fill(c_High) } else { fill(c_Info) }
text(t_Undo, infoSide / 2, BL4 + MAR);
if (numb) { fill(c_High) } else { fill(c_Info) }
text(t_Number, infoSide / 2, BL3 + MAR);
if (best) { fill(c_High) } else { fill(c_Info) }
text(t_Best, infoSide / 2, BL2 + MAR);
if (test) { fill(c_High) } else { fill(c_Info) }
text(t_Test, infoSide / 2, BL1 + MAR);
}
function trackLength(track, mm = mode) {
let h = 0;
for (let i = 0; i < n - 1; i++) {
h += d[track[i]][track[i + 1]];
}
if (mm == 'ROUND') { h += d[0][track[n - 1]] }
return h;
}
function randomInteger(from, to, exept) {
let v = floor(random(to - from + 1)) + from;
if (exept === undefined) return v;
while (v === exept) {
v = floor(random(to - from + 1)) + from;
}
return v;
}
function cityPos(track, cityNum) {
for (let i = 1; i < n; i++) {
if (track[i] == cityNum) { return i }
}
return 0;
}
function modifyTrack() {
if (swapCity2 == 0) { return }
let swapCityPos1, swapCityPos2;
if (mode == 'ROUND') {
swapCityPos1 = cityPos(RoundTest, swapCity1);
swapCityPos2 = cityPos(RoundTest, swapCity2);
RoundTest[swapCityPos1] = swapCity2;
RoundTest[swapCityPos2] = swapCity1;
RoundTestTrack = trackLength(RoundTest);
if (RoundTestTrack < RoundBestTrack) {
RoundBestTrack = RoundTestTrack;
copySource2Target(RoundTest, RoundBest);
}
RoundClosing = d[0][RoundTest[n - 1]];
if (RoundTestTrack - RoundClosing < LinearBestTrack) {
LinearBestTrack = RoundTestTrack - RoundClosing;
LinearTestTrack = LinearBestTrack;
copySource2Target(RoundBest, LinearTest);
copySource2Target(RoundBest, LinearBest);
}
if (RoundBest[n - 1] == n - 1 && RoundTestTrack - RoundClosing < FarestBestTrack) {
FarestBestTrack = RoundTestTrack - RoundClosing;
FarestTestTrack = FarestBestTrack;
copySource2Target(RoundBest, FarestTest);
copySource2Target(RoundBest, FarestBest);
}
}
if (mode == 'LINEAR') {
swapCityPos1 = cityPos(LinearTest, swapCity1);
swapCityPos2 = cityPos(LinearTest, swapCity2);
LinearTest[swapCityPos1] = swapCity2;
LinearTest[swapCityPos2] = swapCity1;
LinearTestTrack = trackLength(LinearTest);
if (LinearTestTrack < LinearBestTrack) {
LinearBestTrack = LinearTestTrack;
copySource2Target(LinearTest, LinearBest);
}
LinearClosing = d[0][LinearTest[n - 1]];
if (LinearTestTrack + LinearClosing < RoundBestTrack) {
RoundBestTrack = LinearTestTrack + LinearClosing;
RoundTestTrack = RoundBestTrack;
copySource2Target(LinearBest, RoundTest);
copySource2Target(LinearBest, RoundBest);
}
if (LinearBest[n - 1] == n - 1 && LinearTestTrack < FarestBestTrack) {
FarestBestTrack = LinearTestTrack;
FarestTestTrack = FarestBestTrack;
copySource2Target(LinearBest, FarestTest);
copySource2Target(LinearBest, FarestBest);
}
}
if (mode == 'FAREST') {
swapCityPos1 = cityPos(FarestTest, swapCity1);
swapCityPos2 = cityPos(FarestTest, swapCity2);
FarestTest[swapCityPos1] = swapCity2;
FarestTest[swapCityPos2] = swapCity1;
FarestTestTrack = trackLength(FarestTest);
if (FarestTestTrack < FarestBestTrack) {
FarestBestTrack = FarestTestTrack;
copySource2Target(FarestTest, FarestBest);
}
// FarestClosing is constant
if (FarestTestTrack + FarestClosing < RoundBestTrack) {
RoundBestTrack = FarestTestTrack + FarestClosing;
RoundTestTrack = RoundBestTrack;
copySource2Target(FarestBest, RoundTest);
copySource2Target(FarestBest, RoundBest);
}
if (FarestTestTrack < LinearBestTrack) {
LinearBestTrack = FarestTestTrack;
LinearTestTrack = LinearBestTrack;
copySource2Target(FarestBest, LinearTest);
copySource2Target(FarestBest, LinearBest);
}
}
lastSwapCity1 = swapCity1;
lastSwapCity2 = swapCity2;
markedCity[swapCity1] = 0;
swapCity1 = 0;
swapCity2 = 0;
}
function copySource2Target(source, target) {
for (let i = 0; i < n; i++) {
target[i] = source[i];
}
}
function clickedCity(x, y) {
for (let i = 1; i < n; i++) {
if (dist(x, y, city[i].x, city[i].y) < R) { return i }
}
return 0;
}
function clearSwapCity() {
swapCity1 = 0;
swapCity2 = 0;
lastSwapCity1 = 0;
lastSwapCity2 = 0;
}
function keyPressed() {
// CITY number decrease
if (keyCode == LEFT_ARROW) {
if (n > 4) { n--; startGame(); }
}
// CITY number increase
if (keyCode == RIGHT_ARROW) {
if (n < 40) { n++; startGame(); }
}
// SWAP / CHAIN
if (keyCode == SPACE) {
if (method == 'SWAP') { method = 'CHAIN' } else { method = 'SWAP' }
}
if (keyCode == 49 && mode != 'ROUND') { clearSwapCity(); mode = 'ROUND'; } // 1
if (keyCode == 50 && mode != 'LINEAR') { clearSwapCity(); mode = 'LINEAR'; } // 2
if (keyCode == 51 && mode != 'FAREST') { clearSwapCity(); mode = 'FAREST'; } // 3
// NEW
if (keyCode == ENTER) { startGame() }
// AGAIN
if (keyCode == BACKSPACE) {
for (let i = 1; i < n; i++) {
RoundTest[i] = i;
RoundBest[i] = i;
LinearTest[i] = i;
LinearBest[i] = i;
FarestTest[i] = i;
FarestBest[i] = i;
}
resetTracks();
}
// COPY
if (keyCode == INSERT) {
copySource2Target(RoundBest, RoundTest);
RoundTestTrack = RoundBestTrack;
copySource2Target(LinearBest, LinearTest);
LinearTestTrack = LinearBestTrack;
copySource2Target(FarestBest, FarestTest);
FarestTestTrack = FarestBestTrack;
}
// UNDO
if (keyCode == ESCAPE && lastSwapCity1 != 0) {
swapCity1 = lastSwapCity1;
swapCity2 = lastSwapCity2;
}
if (keyCode == CONTROL) { numb = !numb }
if (keyCode == HOME) { best = !best }
if (keyCode == END) { test = !test }
// EN / HU
if (keyCode == SHIFT) {
if (lang == 'EN') { lang = 'HU' } else { lang = 'EN' }
setTitles();
}
}
function mousePressed() {
let X = mouseX;
let Y = mouseY;
if (X > infoSide) {
let cl_City = clickedCity(X, Y);
// No City
if (cl_City == 0) { return }
// Farest City in FAREST mode
if (cl_City == n - 1 && mode == 'FAREST') { return }
// First City
if (swapCity1 == 0) {
swapCity1 = cl_City;
markedCity[swapCity1] = 1;
return;
}
// First City again
if (swapCity1 == cl_City) {
markedCity[swapCity1] = 0;
swapCity1 = 0;
return;
}
// Second City
if (swapCity1 != 0) { swapCity2 = cl_City; return; }
return;
}
if (Y < TL1) {
if (X < infoSide / 2 && n > 4) { n--; startGame(); }
if (X > infoSide / 2 && n < 40) { n++; startGame(); }
}
if (Y > TL1 && Y < TL2) {
if (method == 'SWAP') { method = 'CHAIN' } else { method = 'SWAP' }
}
if (Y > TL2 && Y < TL3 && mode != 'ROUND') { clearSwapCity(); mode = 'ROUND'; }
if (Y > TL3 && Y < TL4 && mode != 'LINEAR') { clearSwapCity(); mode = 'LINEAR'; }
if (Y > TL4 && Y < TL5 && mode != 'FAREST') { clearSwapCity(); mode = 'FAREST'; }
// NEW
if (Y > BL7 && Y < BL6) { startGame() }
// AGAIN
if (Y > BL6 && Y < BL5) {
for (let i = 1; i < n; i++) {
RoundTest[i] = i;
RoundBest[i] = i;
LinearTest[i] = i;
LinearBest[i] = i;
FarestTest[i] = i;
FarestBest[i] = i;
}
resetTracks();
}
// COPY
if (Y > BL5 && Y < BL4) {
copySource2Target(RoundBest, RoundTest);
RoundTestTrack = RoundBestTrack;
copySource2Target(LinearBest, LinearTest);
LinearTestTrack = LinearBestTrack;
copySource2Target(FarestBest, FarestTest);
FarestTestTrack = FarestBestTrack;
}
// UNDO
if (Y > BL4 && Y < BL3) {
if (lastSwapCity1 != 0) {
swapCity1 = lastSwapCity1;
swapCity2 = lastSwapCity2;
}
}
if (Y > BL3 && Y < BL2) { numb = !numb } // NUMBER
if (Y > BL2 && Y < BL1) { best = !best } // BEST
if (Y > BL1) { test = !test } // TEST
}
function draw() {
background(c_Back);
noFill();
if (mode == 'ROUND') {
if (best) { drawTrack(RoundBest, c_RoundBest, w_Best) }
if (test) { drawTrack(RoundTest, c_RoundTest, w_Test) }
}
if (mode == 'LINEAR') {
if (best) { drawTrack(LinearBest, c_LinearBest, w_Best) }
if (test) { drawTrack(LinearTest, c_LinearTest, w_Test) }
}
if (mode == 'FAREST') {
if (best) { drawTrack(FarestBest, c_FarestBest, w_Best) }
if (test) { drawTrack(FarestTest, c_FarestTest, w_Test) }
}
drawCity();
drawInfo();
modifyTrack();
}