Skip to content

Instantly share code, notes, and snippets.

@dzaima
Created March 4, 2018 12:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dzaima/eb36fd6ca55b4968277f33a210a8ab3f to your computer and use it in GitHub Desktop.
Save dzaima/eb36fd6ca55b4968277f33a210a8ab3f to your computer and use it in GitHub Desktop.
const DEBUG = false;
//raw directions
const UL = 0; const U = 1; const UR = 2;
const L = 3; const C = 4; const R = 5;
const DL = 6; const D = 7; const DR = 8;
//directions from the reference point
const ul = 16; const u = 17; const ur = 18;
const l = 19; const c = 20; const r = 21;
const dl = 22; const d = 23; const dr = 24;
const rp = 16;
const NOP = {cell:C};
var toReturn = undefined;
var foods;
var ref;
const me = view[C].ant;
const type = me.type;
const food = me.food;
const isQueen = type == 5;
const allvs = allRots(view);
const ixs = [0,1,2,3,4,5,6,7,8];
const cols = ixs.map(a=>view[a].color);
const allcs = allRots(cols);
// color 0 is ignore
// negative colors are always matched as correct but need to be repaired
const COLORS = [1, 6, 2, 3, 4, 5, 8, 7];
const WH = COLORS[0]; // 1
const C1 = COLORS[1]; // 6/should stay green (or whatever is trail-eraser most ignorant of :p)
const C2 = COLORS[2]; // 2/yellow
const C3 = COLORS[3]; // 3/purple
const C4 = COLORS[4]; // 4/cyan
const C5 = COLORS[5]; // 5/red
const C6 = COLORS[6]; // 8/black
const C7 = COLORS[7]; // 7/blue
const TRAIL = C1;
const PatternKeep = ["vp", "hp", "p"];
// spam `for (let cv of PatternKeep) pattern[cv] = opattern[cv];` everywhere
const QUEEN = {ant:{friend:true,type:5}};
const MU = 1;
const MD = 2;
const mydir = type==MU? u : d;
const notmydir = type==MU? d : u;
var refScorer;
{
let scoreTotal = 0;
let totalBiggestScore = 0;
let ptt;
let correct = 0;
let incorrect = 0;
let corrstr = "";
let corr = [];
refScorer = {
new: () => {scoreTotal = 0; correct = 0; corrstr = ""; corr = []; incorrect = 0},
add: (real, wanted, where, pattern) => {
ptt = pattern;
let eqt = cellsEq(real, wanted);
if (eqt && eqt!==2) correct++;
if (!eqt) incorrect++;
corrstr+= eqt?"#":" ";
corr.push(eqt);
},
finish: (currBest) => {
if (incorrect == 0) scoreTotal+= 1e4;
if (ptt.p == 0 && ptt.vp ==-2 && !/......###/.test(corrstr)) scoreTotal-= 1e300;
if (ptt.p == 0 && ptt.vp == 2 && !/###....../.test(corrstr)) scoreTotal-= 1e300;
if (correct > 5) correct+= 3;
if (ptt.p==1) {
var good = false;
if (mydir==u || food) good|= /...######/.test(corrstr);
if (mydir==d || food) good|= /######.../.test(corrstr);
if (good) correct*=1.5;
else scoreTotal-= 20;
}
if (corr[4]) [[0,1,3],[1,2,5],[3,6,7],[5,7,8]].forEach(c=> {
if (c.every(i=>corr[i])) scoreTotal+= 3;// correct++;
});
var fc = (what) => (["u","c","d"][(what-17)/3])
console.log(`P[${ptt.p}@${ptt.hp} ${ptt.vp}] score = ${correct+scoreTotal/10}/${totalBiggestScore} [${corrstr}]`, ptt);
if (totalBiggestScore < correct+scoreTotal/10) totalBiggestScore = correct+scoreTotal/10;
return correct+scoreTotal/10;
}
};
}
const rail = [[-1,-1,C5,-1],
[C4,-1,-1,-1],
[C1,C1,C2,C3],
[C4,C5,C1,C6],
[C2,C7,C3,C1],
[C4,-1,-1,-1],
[-1,-1,C5,-1]];
rail.vp = -2;
rail.hp = 0;
rail.p = 0;
const shaft = [[WH,-1,C5],
[C4,-1,WH]];
const foodExt = [C7,C3,C6];
shaft.p = 1;
shaft.vp = 0;
shaft.hp = 0;
const pattern = {or:[]};
const shifts = translates(rail, R);
function addPart(part) {
pattern.or.push(setSize(part, 3, 3));
}
function section(line, forMe) {
if (!forMe) return [];
shifts.forEach(c=>{
let res = c.slice(line+2);
for (let cv of PatternKeep) res[cv] = c[cv];
res.vp = line;
addPart(res);
});
}
section(-2, !isQueen);
section(-1, !isQueen);
section( 0, true);
section( 1, !isQueen);
section( 2, !isQueen);
if (!isQueen) {
var shafts = translates(shaft,U).map(c => setSize(c, 3, 3));
var backFromFood = [foodExt,[0,0,0],[0,0,0]];
backFromFood.p = 2;
if (food) addPart(backFromFood);
// if (mydir==u || food) shafts.forEach(c=>addPart(mapP(c,(ln,i)=>i==0? ln.map(col=>-Math.abs(col)) : ln)));
// if (mydir==d || food) shafts.forEach(c=>addPart(mapP(c,(ln,i)=>i==2? ln.map(col=>-Math.abs(col)) : ln)));
shafts.forEach(c=>addPart(c));
}
console.log("new ant");
reference(pattern, undefined, refScorer);
const vp = ref.pt.vp;
const hp = ref.pt.hp;
const p = ref.pt.p;
//log("ant",type,"on rail at ", ref);
//log("vp", vp, "hp", hp);
//-----------------------------------------------------QUEEN-------------------------------------------------------
if (isQueen) {
if (food < 2 && ref.dist > 5) {
//initial food scramble
generateFoods();
var sidevar = -1;
if ([0,2,6,8].some((i) => {
if (cols[i] === TRAIL) {
if (sidevar === -1)
sidevar = i;
else return true;
}
})) move(0);
else if (foods.includes(1)) {
move(foods.indexOf(1));
} else if (cols[C] == TRAIL) {
if (sidevar == -1) {
move(0);
} else {
move(8-sidevar);
}
} else {
color(C, TRAIL);
}
//end food scramble
} else {
//on the rail
if ((food > 100 || ref.dist != 1 || !spawnMiners()) &&
(p == 0 && repair())) {
if (hp != 3 || get(ur).ant || get(dr).ant) {
move(r);
} /*else if (food < 50) {
if (hp == 1 && !random4()) spawnMiners();
}*/
}
}
} else {
const movedir = food? notmydir : mydir;
const unmovedir = food? mydir : notmydir;
if (p == 0) {
if ((hp==0 && (vp == (mydir==u?-1:1)) && find(QUEEN) == notmydir) || repair()) {
var fQ = find(QUEEN);
var queenvp = fQ? (((fQ-1)%3) + hp + 16)%4: -1;
if (food && vp == 0) move([r,ur,dr]);
//else if (!food && vp == 2 && mydir==u) move([ur,r]);
//else if (!food && vp ==-1 && mydir==u && cellsEq(get(dr),{ant:"queen"})) move(r);
//else if (!food && vp == 1 && mydir==d && cellsEq(get(ur),{ant:"queen"})) move(r);
//else if (!food && vp ==-2 && mydir==d) move([dr,r]);
else if (queenvp == 0) move(r); // move into place for shafting
else if (hp==0 && (vp == (mydir==u?-1:1)) && fQ == notmydir) {if (ref.dist == 0) move(mydir)} // go out to shaft!
else if (queenvp !=-1) move(c); // else don't move around queen
else if ((food || fQ != u) && vp == 1) move(mydir==d? [r, ur] : [ur, u]); // move forwards
else if ((food || fQ != d) && vp ==-1) move(mydir==u? [r, dr] : [dr, d]);
else if ((hp==0 && Math.abs(vp) > 1) || vp == 0) move([movedir, unmovedir]); // move more away from rail into the shaft
else if (vp==0) move([r,mydir+1,notmydir+1]); // move forwards
else move(r); // TODO: I don't remember ¯\_(ツ)_/¯
}
} else if (p == 1) {
if (get([l,r]).some(c=>c.food)) {
var foodL = get(l).food;
var shaftsn = shafts.map(c=>c.map((ln,i)=>[ln[0], foodL? foodExt[i] : foodExt[2-i], ln[2]]));
for (let cv of PatternKeep) shaftsn[cv] = shafts[cv];
reference({or:shaftsn}, undefined, refScorer);
if (repair([c, mydir, notmydir])) {
move(foodL? l : r);
}
}
if (
!(
(cellsEq(get(mydir), {ant:{type:type,friend:true,food:0}}) && get([mydir+1,mydir-1]).some(c=>c.food)) // someone's preparing to get food
||
get([mydir+1,mydir-1]).some(c=>cellsEq(c,{ant:{type:type,friend:true,food:1}})) // someone got food!
||
cellsEq(get(mydir), {ant:{type:type,friend:true,food:0}, color:[foodExt[0],foodExt[2]]}) //someone way above is getting food
) && repair()) {
move([movedir,unmovedir]);
}
} else if (p == 2) {
move(u);
} else {
ohno("no p for me..");
}
}
function spawnMiners() {
if (view.filter(c=>c.ant).length>3) return false; // we have enough
var du = find({ant:{type:MU,friend:true}}); // do up / down
var dd = find({ant:{type:MD,friend:true}});
if (du&&!dd) return spawn(d,MD) || spawn(u,MU);
if (dd&&!du) return spawn(u,MU) || spawn(d,MD);
if (random4()<2) return spawn(u,MU) || spawn(d,MD);
else return spawn(d,MD) || spawn(u,MU);
}
if (!toReturn) toReturn = NOP;
return toReturn;
//---------------------------------helper functions below---------------------------------
function mapP(pt,fn) {
var res = pt.slice().map(c=>c.slice()).map(fn);
for (let cv of PatternKeep) res[cv] = pt[cv];
return res;
}
function diagonalSearch (cl) {
var colors = view.map(c=>c.color === cl);
if (!colors.includes(true)) return color(UL, cl);
const nc = [WH,C1,C2,C3,C4,C5,C6,C7].filter(c => c !== cl);
if (exactReference([[cl,nc,nc],
[nc,cl,nc],
[nc,nc,nc]])) return move(dr);
if (exactReference([[cl,nc,nc],
[nc,nc,nc],
[nc,nc,nc]])) return color(C, cl);
move(random4()? U : UL);
}
function generateFoods (useRef) {
if (!foods || useRef)
foods = ixs.map(a=>view[a].food);
if (useRef) {
foods = allRots(foods)[ref.rot];
}
}
/*
false - ??
true - gonna draw the color
1 - already colored
*/
function obstacle (where) {
var got = get(where);
return got.ant || (type !== QUEEN && got.food);
}
/*
false - coloring in this move
0 - incorrect params?!?!!??!
true - already colored
*/
function color (where, color) {
if (where&rp) where = rawp(where);
if (get(where).color === color) return true;
return result({cell:where, color:color})? false : 0;
}
function move (where) {
if (Array.isArray(where)) {
return where.some(move /*is possible*/);
}
if (where&rp) where = rawp(where);
return result({cell:where});
}
function spawn (where, what, force) {
if (Array.isArray(where)) {
return where.some(c => spawn(c, what, force));
}
if (where&rp) where = rawp(where);
return result({cell:where, type:what}, force);
}
/*
either colors the cell white or leaves it correctly colored.
Meant for making it as an okay thing to stay like that when moving around
false - coloring...
true - ready for moving to related!
*/
function clear (cell) {
if (correct(cell)) return true;
else return color(cell, WH);
}
function correct (cell) {
cell = cell&~rp;
return cellsEq(ref.view[cell], ref.pt[cell]);
}
function fix (cell) {
cell&= ~rp;
let ccolor = defColor(ref.pt[cell]);
if (ccolor != ref.view[cell].color) {
color(cell|rp, ccolor);
return false;
} else return true;
}
function repair (firstdirs) {// firstdirs is lowercase
//if (ref.dist == 0) return true;
var possibilities = [];
var repaired = ref.pt.every((shouldbe, index) => {//return if every cell is correct
if (!cellsEq(ref.view[index], shouldbe, true)) {
let ccolor = defColor(shouldbe);
if (ccolor != ref.view[index]) {
if (!firstdirs) {
color(index|rp, Math.abs(ccolor));
return false;
} else possibilities.push({i:index|rp, c:ccolor});
}
}
return true;
});
if (repaired && possibilities.length > 0) {
repaired = false;
var first = firstdirs.find(c=>possibilities.find(pc=>pc.i==c));
if (first) color(first, possibilities.find(pc=>pc.i==first).c);
else color(possibilities[0].i, possibilities[0].c);
}
return repaired;
}
function defColor (cell) {
if (typeof cell === "number") return cell;
if (Array.isArray(cell)) return cell[0];
ohno("defColor recieved bad param type ", cell);
}
function get (pos) {
if (Array.isArray(pos)) return pos.map(get);
if (pos&rp) return ref.view[pos^rp];
else return view[pos];
}
function gc (pos) {
return get(pos).color;
}
function exactReference (...args) {
var pt = match(...args);
if (pt.dist === 0) {
return ref = pt;
}
return false;
}
function reference (...args) {
return ref = match(...args);
}
/*
this function makes a pseudo-reference out of a value of a direction
For example, if you want a type-2 worker orthogonally to you to be able to be referenced as `r` (and of course the other directions are relative), do
define(r, find({ant:{type:2,friend:true}}))
*/
function define (whichDir, whatCellItIs) {
var rot = [0,1,2,3].find(rot => rotate(whatCellItIs, -rot) === (whichDir&~rp));
ref = {rot:rot, view:allvs[rot]};
}
function match (pattern, givendir, scoreResults) {
if (Array.isArray(pattern.or)) {
var bestPattern = {dist:999, correct: -1, score: -1e308};
pattern.or.forEach((cOr) => {
let cres = match(cOr, givendir, scoreResults);
if (scoreResults? cres.score > bestPattern.score : compareScores(cres.dist, cres.correct, bestPattern.dist, bestPattern.correct) > 0) {
bestPattern = cres;
}
});
//console.log(bestPattern);
return bestPattern;
}
var bestRotation = 0;
var bestDist = 999;
var bestCorrect = -1;
var bestScore = -1e308;
if (Array.isArray(pattern)) {
let opattern = pattern;
pattern = [...pattern[0],...pattern[1],...pattern[2]];
for (let cv of PatternKeep) pattern[cv] = opattern[cv];
((givendir != undefined)? [allvs[givendir]] : allvs).forEach((cVRot, cRot) => {
if (givendir != undefined) cRot = givendir;
let dist = 0;
let correct = 0;
let fatalFail = false;
if (scoreResults) scoreResults.new();
// log("match going trough", cVRot.map(c=>c.color), "at", pattern, pattern.hp);
cVRot.some((cVCell, cCell) => {//log(cCell, pattern)
let eqt = cellsEq(cVCell, pattern[cCell]);
if (scoreResults) scoreResults.add(cVCell, pattern[cCell], cCell, pattern);
else {
if (eqt === 3) correct+=2;
if (eqt === 0) fatalFail = true;
}
if (eqt === true) correct++;
if (eqt === false) dist++;
return fatalFail;
});
//log("distance",dist, "correct",correct);
let score;
if (scoreResults? (score = scoreResults.finish(bestScore)) > bestScore : !fatalFail && compareScores(dist, correct, bestDist, bestCorrect)>0) {
bestDist = dist;
bestCorrect = correct;
bestRotation = cRot;
if (scoreResults) bestScore = score;
}
});
return {dist:bestDist, correct:bestCorrect, rot:bestRotation, pt:pattern, view:allvs[bestRotation], opt:opattern, score:bestScore};
}
ohno("match failed to do anything", pattern);
}
function compareScores (ad, ac, bd, bc) {
//scoring by percentage correct
let a = ac / (ad+ac+1);
let b = bc / (bd+bc+1);
//scoring by correct overhead
///let a = ac - ad
///let b = bc - bd
return a==b? 0 : ((a>b)? 1 : -1);
}
function bitify (num) {
num&=~rp;
if (num === U) return bU;
if (num === D) return bD;
if (num === L) return bL;
if (num === R) return bR;
if (num === UL) return bUL;
if (num === DL) return bDL;
if (num === UR) return bUR;
if (num === DR) return bDR;
}
function rawp (dir) {
dir &= ~rp; // remove rp 'flag'
return rotate(dir, ref.rot);
}
function refp (dir) {
return rp | rotate(dir, -ref.rot);
}
function rotate (torot, am) {
am = am%4;
if (am < 0) am+= 4;
for (let i = 0; i < am; i++) torot = [2,5,8, 1,4,7, 0,3,6][torot];
return torot;
}
function find (obj) {
var res = ref.view.findIndex(c => cellsEq(c, obj));
if (res === -1) return false;
return res | rp;
}
/*
0 - ant isn't how it should be
false - colors don't match
true - match
2 - match, but very easy to match, so shouldn't count
3 - matched ants
1st param - full object, aka anything that comes from view[x]
2nd param - the partical object you want it to equal (e.g. {ant:{friend:true}}; 6 )
*/
function cellsEq (full, required, negativeIncorrect) { // log(full, new Error().stack);
if (typeof required === "number") {
if (required === 0) return 2;
if (!negativeIncorrect && required < 0) return 2;
if (Math.abs(required) === full.color) return true;
return false;
} else if (Array.isArray(required)) {
let best = 0;
required.some((c) => {
let res = cellsEq(full, c);
if (res === false && best === 0) best = false;
if (res === true && best !== 3) best = true;
if (res === 3) best = 3;
});
//log("cellsEq array",required,full,best);
return best;
} else if (typeof required === "object") {
if (required.ant==false && full.ant) return 0;
if (required.ant) {
if (required.ant==="notQueen") {
if (full.ant) {
if (full.ant.type==5 && full.ant.friend) return 0;
}
} else if (required.ant==="worker") {
if (full.ant) {
if (full.ant.type==5 && full.ant.friend) return 0;
} else return 0;
} else if (required.ant==="queen") {
if (full.ant) {
if (full.ant.type!=5 && full.ant.friend) return 0;
} else return 0;
} else {
if (!full.ant) return 0;
else {
// ಠ_ಠ
if (required.ant.type && required.ant.type != full.ant.type) return 0;
if (required.ant.friend && required.ant.friend != full.ant.friend) return 0;
}
}
}
if (required.color) {
if (Array.isArray(required.color)) {
if (!required.color.some(c=>c===full.color)) return false;
} else if (required.color !== full.color) return false;
}
if (required.food !== undefined && full.food != required.food) return false; // intentional `!=`
if (required.ant) return 3;
return true;
}
ohno("cellsEq failed to do anything", arguments);
}
function limitSize (pattern, xs, ys) {
var out = pattern.slice(0, ys).map(c => c.slice(0,xs));
for (let cv of PatternKeep) out[cv] = pattern[cv];
return out;
}
function setSize (pattern, xs, ys) {
//var out = pattern.slice(0, ys).map(c => c.slice(0,xs));
//while (out[0].length < xs) out.map(c => c.push());
var out = [];
for (let y = 0; y < ys; y++) {
var ca = [];
for (let x = 0; x < xs; x++) {
ca.push(pattern[y % pattern.length][x % pattern[0].length]);
}
out.push(ca);
}
for (let cv of PatternKeep) out[cv] = pattern[cv];
return out;
}
function translates (pattern, direction) {
var out = [pattern.slice()];
for (let cv of PatternKeep) out[0][cv] = pattern[cv];
for (let i = 0; i < lengthIn(pattern, direction)-1; i++) {
pattern = rotatePt(pattern, direction);
out.push(pattern);
}
return out;
}
function rotatePt (pattern, direction) {
if (direction == DR) {
return rotatePt(rotatePt(pattern, D), R);
}
if (direction == R) {
let np = pattern.map((c, ln) => c.slice(1).concat([c[0]]));
for (let cv of PatternKeep) np[cv] = pattern[cv];
np.hp++;
return np;
}
if (direction == D) {
let np = pattern.slice(1).concat([pattern[0]]);
for (let cv of PatternKeep) np[cv] = pattern[cv];
np.v++;
return np;
}
if (direction == U) {
let np = pattern.slice();
np.splice(0,0,...np.splice(np.length-1));
for (let cv of PatternKeep) np[cv] = pattern[cv];
np.vp--;
return np;
}
ohno("rotatePt failed to do anything", arguments);
}
function translatePt (pattern, direction) {
direction&=~rp;
if (direction == R) {
let np = pattern.map((ln) => ln.slice(1).concat(-1));
for (let cv of PatternKeep) np[cv] = pattern[cv];
np.hp++;
return np;
}
if (direction == L) {
let np = pattern.map((ln) => [-1].concat(ln.slice(0,-1)));
for (let cv of PatternKeep) np[cv] = pattern[cv];
np.hp--;
return np;
}
if (direction == U) {
let np = [new Array(pattern[0].length).fill(-1)].concat(pattern.slice(0,-1));
for (let cv of PatternKeep) np[cv] = pattern[cv];
np.vp--;
return np;
}
if (direction == D) {
let np = pattern.slice(1).concat([new Array(pattern[0].length).fill(-1)]);
for (let cv of PatternKeep) np[cv] = pattern[cv];
np.vp++;
return np;
}
ohno("translatePt failed to do anything", arguments);
}
function lengthIn (pattern, direction) {
if (direction == U || direction == D) return pattern.length;
if (direction == L || direction == R) return pattern[0].length;
ohno("lengthIn failed to do anything", arguments);
}
function ohno (msg) {
log(new Error());
log("FAILURE");
log(...arguments);
if (DEBUG) throw msg;
else toReturn = NOP;
}
function log() {
if (DEBUG) console.log(...arguments);
}
//---------------------------------functions from Rail Miners---------------------------------
function result (action, force) {
if (force) return resultForce(action);
if (toReturn !== undefined) return 0;
var color = action.color;
var type = action.type;
var cell = action.cell;
if (!(cell >= 0 && cell <= 8)) return false;
if (!color && ((view[cell].ant && cell != 4) || (isQueen? (view[cell].food && type) : (food && view[cell].food)))) return false;
if (!isQueen && type) return false;
if (!isQueen && !color && food && view[cell].food) return false;
if (isQueen && !food && type) return false;
if (type && cell==C) return false;
if (color && type) return false;
toReturn = action;
return true;
}
function resultForce(action) {
var temptoReturn = toReturn;
toReturn = undefined;
if (result(action)) {
return true;
} else {
toReturn = temptoReturn;
return false;
}
}
function random4 () {
var scores = allcs.map((cs) => {
let cscore = 0;
cs.forEach((c) => {
cscore*= 8;
cscore+= c-1;
})
return cscore;
})
var bestscore = -1, bestindex = 1;
scores.forEach((score, index) => {
if (score > bestscore) {
bestscore = score;
bestindex = index;
}
})
return bestindex;
}
function allRots (arr) {
return [arr,
[arr[2], arr[5], arr[8],
arr[1], arr[4], arr[7],
arr[0], arr[3], arr[6]],
[arr[8], arr[7], arr[6],
arr[5], arr[4], arr[3],
arr[2], arr[1], arr[0]],
[arr[6], arr[3], arr[0],
arr[7], arr[4], arr[1],
arr[8], arr[5], arr[2]]];
}
function rotate1CW (c) {
return [1, 2, 5, 0, undefined, 8, 3, 6, 7][c];
}
function rotate1CCW (c) {
return [3, 0, 1, 6, undefined, 2, 7, 8, 5][c];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment