Skip to content

Instantly share code, notes, and snippets.

@dzaima
Last active February 26, 2018 19:06
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/193b9a539269a3af63a1df7430ebba94 to your computer and use it in GitHub Desktop.
Save dzaima/193b9a539269a3af63a1df7430ebba94 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;
//binary directions
const bUL = 3; const bU = 1; const bUR = 5;
const bL = 2; const bC = 0; const bR = 4;
const bDL = 10; const bD = 8; const bDR = 12;
const rp = 16;
const NOP = {cell:C};
var toReturn = undefined;
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 -1 is ignore
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", "part", "side"];
// spam `for (let cv of PatternKeep) pattern[cv] = opattern[cv];` everywhere
//entry code starts here
const moveSide = r;
const backtrackSide = r;
const throttleQueensSpeed = true;
//[[2,6,2],[7,4,7],[2,3,2],[4,6,4],[5,3,5],[1,4,1],[7,6,7]]
const roadmap = [[6,2,6],[7,1,7],[3,5,3],[6,2,6],[1,4,1],[7,5,7],[6,2,6]]//[[4,1,4],[6,7,6],[3,5,3],[6,2,6],[7,3,7],[1,4,1],[7,2,7]]
.map(l=>l.slice(0,2).map(c=>COLORS[c]));
const RU = roadmap.slice(0, 3);
const RD = roadmap.slice(4, 7);
const RC = [//RU[1],
RU[2],
roadmap[3],
RD[0],
/*RD[1]*/];
// Alions
//const RU = [[C1,C2,C3,C4,C5],
// [C3,C4,C5,C1,C2],
// [C5,C1,C2,C3,C4],
// [C2,C3,C4,C5,C1],
// [C4,C5,C1,C2,C3]];
//const RD = RU.slice().reverse();
//const RC = [
// RU[3],
// RU[4],
// [C1,C2,C3,C4,C5],
// RD[0],
// RD[1],
// ];
console.log("----------------------------------");
console.log(type,food,view);
RC.hp = 0;
RC.vp = 0;
RC.part = c;
RC.side = c;
RU.hp = 0;
RU.vp = 0;
RU.part = u;
RU.side = u;
RD.hp = 0;
RD.vp = 0;
RD.part = d;
RD.side = d;
const QUEEN = 5;
const MU = 1;
const MD = 2;
const mySide = type==MU? u : d;
const otherSide = type==MU? d : u;
const RCtR = translates(RC, R);
const translates2D = what => translates(what, R).map(c => translates(c, U)).reduce((a,b)=>a.concat(b));
const rail = {or:
[
//...RCtR.map(cp => {var res=cp.slice(1); for (let cv of PatternKeep) res[cv] = cp[cv]; res.side = c; return res}), // center
...RCtR, // up
//...RCtR.map(cp => {var res=cp.slice(2); for (let cv of PatternKeep) res[cv] = cp[cv]; res.side = d; return res}), // down
...(/*type==MD? [] : */translates2D(RU)),
...(/*type==MU? [] : */translates2D(RD)),
].map(c => setSize(c, 3, 3))
};
var foods;
var ref;
var refScorer;
{
let scoreTotal = 0;
let ptt;
let correct = 0;
let corrstr = "";
let corr = [];
let trustMap = [[ .6, .6,.5],
[1.2, 1.2, 1],
[ 2, 2, 1]];
refScorer = {
new: () => {scoreTotal = 0; correct = 0; corrstr = ""; corr = [];},
add: (real, wanted, where, pattern) => {
ptt = pattern;
let eqt = cellsEq(real, wanted);
if (eqt) correct++;
corrstr+= eqt?"#":" ";
corr.push(eqt);
//if (pattern.part == c) scoreTotal += (eqt? 2 : -1) * (pattern.side==c && L<=where && where<=R? 3 : 1);
//else scoreTotal += (eqt === 2? 0.1 : eqt? (pattern.part!=mySide? 0.25 : 1) : -.5) * trustMap[(pattern.part==u? x=>x : x=> 2-x) (Math.floor(where/3))][where%3];
// else scoreTotal += (eqt? 1 : -1) * trustMap[(pattern.part==u? 0 : 3) + (pattern.part==u? 1 : -1) * Math.floor(where/3)][where%3];
},
finish: (currBest) => {
if (correct == 9) scoreTotal = 1e308;
if (correct > 5) correct+= 3;
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++;
});
if (ptt.part==c) {
if (type==QUEEN) scoreTotal+= 5;
else if (!food) scoreTotal-= 10;
else scoreTotal-= 2;
}
if (ptt.side == c) {
if (corrstr.slice(0,3).match(/###/) && corrstr.slice(6,9).match(/###/)) scoreTotal+= 5;
}
if (cols.filter(c=>c!==WH).length < 2) scoreTotal = 1e308;
var fc = (what) => (["u","c","d"][(what-17)/3])
//if (scoreTotal>0) console.log((scoreTotal > currBest? "!" : " ")+`P[+${fc(ptt.part)}|${fc(ptt.side)}@${ptt.vp} ${ptt.hp}] score =`,scoreTotal,"/",currBest, ptt, "["+corrstr+"]");
return correct+scoreTotal/10;
}
};
}
reference(rail, undefined, refScorer);
const vP = ref.pt.vp;
const hP = ref.pt.hp;
const Rpart = ref.pt.part;
const Rside = ref.pt.side;
if (isQueen) {
if (food < 2 && ref.correct < 6) {
//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 (repair([C,L,R])) {
if (food && food < 150 && vP == 0 && /*hP == 0 &&*/ (find({ant:{type:MU}}) || find({ant:{type:MD}})) && random4() < 2) {
let foundU = find({ant:{type:MU}});
let foundD = find({ant:{type:MD}});
if (foundU && !foundD) spawn(d, MD);
else if (foundD && !foundU) spawn(u, MU);
else if (random4() == 0) spawn(u, MU);
else if (random4() == 1) spawn(d, MD);
}
// console.log(Rside, "||", Rpart);
if (Rside === c && (!throttleQueensSpeed || random4() < 2 || find({ant:"worker"}))) move([r,u,d]);
else if (Rside === u) {
if (Rpart === c && get(dr).ant) move(r);
else move([d,r]);
}
else if (Rside === d) {
if (Rpart === c && get(ur).ant) move(r);
else move([u,r]);
}
} else {
if (food > 0 && food < 3 && ref.dist == 1) {
let foundU = find({ant:{type:MU}});
let foundD = find({ant:{type:MD}});
if (foundU && !foundD) spawn(d, MD, true);
else if (foundD && !foundU) spawn(u, MU, true);
else if (random4() < 2) spawn(u, MU, true);
else spawn(d, MD, true);
}
}
}
} else if (type == MU || type == MD) {
if (food) {
if (repair()) {
//var blocked = obstacle(backtrackSide) && obstacle(backtrackSide-3) && obstacle(backtrackSide+3);
if (Rside === c) move([backtrackSide, mySide]);
else if (Rside === u) {
if (obstacle(backtrackSide+3)) move([backtrackSide, u]);
else move([d,backtrackSide]);
} else if (Rside === d) {
if (obstacle(backtrackSide-3)) move([backtrackSide, d]);
else move([u,backtrackSide]);
}
}
} else {
const moveSideI = 40-moveSide;
const moveSideAdd = moveSide==r? 1 : -1;
const repairOrder = [ur,u,ul,r,c,l,dr,d,dl].filter(c=>c!=mySide + moveSideAdd/* && c!=moveSide*/).map(c => c&~rp);
if (find({food:1})) {
if (repair(repairOrder)) {
var place = find({food:1});
if (place%2 == 1) move(place);
else move(rotate1CW(rawp(place)));
}
}
else if (find({ant:{type:QUEEN}})) { if (repair(repairOrder)) move(mySide); }
else if (Rside == otherSide) { if (repair(repairOrder)) move(mySide); }
else if (!correct(otherSide + moveSideAdd)) { // if there's a hole in front of me
if (clear(otherSide + moveSideAdd) && [ur,u,ul,r,c,l,dr,d,dl].every(dir => (dir === otherSide+moveSideAdd) || fix(dir)))
move(otherSide);
}
else if (ref.dist == 0) move(mySide);
// else if (!correct(C)) fix(C);
else if (ref.dist == 1 && (!correct(mySide + moveSideAdd) || !correct(moveSide))) { if (clear(mySide + moveSideAdd)) move(/*random4()? mySide : */moveSide); }
else repair(repairOrder);
}
}
if (!toReturn) toReturn = NOP;
return toReturn;
//---------------------------------helper functions below---------------------------------
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 :p*/);
}
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)) {
let ccolor = defColor(shouldbe);
if (ccolor != ref.view[index]) {
if (!firstdirs) {
color(index|rp, 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==c).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) { // log(full, new Error().stack);
if (typeof required === "number") {
if (required === -1) return 2;
if (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 (!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 && required.color !== full.color) return false;
if (required.food !== undefined && full.food !== required.food) return false;
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) {
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