// The Witness play journal day 4
// tsc app.ts && node app.js
let level = [".X...",
".....",
".X.XX",
"X....",
".....",
".X.X.",]
let pieces = [
["111",
"..1"],
["22",
"22"],
["3.",
"33",
".3"],
["4",
"4",
"4",
"4"],
["555",
"5.."],
[".6.",
"666"],
["77.",
".77"]
]
function shallowcopy<T>(x:T[]) { return x.slice(0) } // Copy js array
function pickrandom<T>(x:T[]) : T { // Remove item from js array and return
return x.splice( Math.floor(Math.random() * x.length), 1 )[0]
}
// All strings in a slate are assumed 1-letter. indexes are [y][x]
type Slate = string[][]
function blankslate(target:string[]=level) {
return target.map(x => x.split(''))
}
function printslate(x:Slate) {
x.forEach(function (x:string[]) {
console.log(x.join(''))
})
console.log('')
}
function testslate(x:Slate) { // Puzzle criteria: No Xs visible
return x.reduce(function(previous:boolean, current:string[], _,_2) {
return previous && current.indexOf("X") < 0
},true)
}
class Point { constructor(public x:number, public y:number) {} }
function testpoints(target:string[]=level) : Point[] { // all 30 coordinates
let result = []
for(let y = 0; y < level.length; y++)
for(let x = 0; x < target[0].length; x++)
result.push(new Point(x,y))
return result
}
function mayinsertslate(slate:Slate, piece:string[], at:Point) : boolean {
for(let y = 0; y < piece.length; y++) {
for(let x = 0; x < piece[0].length; x++) {
let sx = x + at.x, sy = y + at.y
if (sy >= slate.length || sx >= slate[0].length) return false
if (piece[y][x] != '.') {
let into = slate[sy][sx]
if (into != '.' && into != 'X') return false
}
}
}
return true
}
function insertslate(slate:Slate, piece:string[], at:Point) {
for(let y = 0; y < piece.length; y++)
for(let x = 0; x < piece[0].length; x++)
if (piece[y][x] != '.')
slate[y+at.y][x+at.x] = piece[y][x]
}
function attempt() {
let slate = blankslate()
let bag = shallowcopy(pieces)
while (bag.length > 0) {
let piece = pickrandom(bag)
let candidates = testpoints()
while (true) {
if (candidates.length == 0) return null;
let candidate = pickrandom(candidates)
if (mayinsertslate(slate, piece, candidate)) {
insertslate(slate, piece, candidate)
break
}
}
}
if (!testslate(slate)) return null
return slate
}
for(let trials = 0;;trials++) {
let slate = attempt()
if (slate) printslate(slate)
if (trials > 0 && trials%10000==0) console.log(trials + " configurations attempted\n")
}
/*
(TOPOGRAPHICALLY) UNIQUE SOLUTIONS FOUND BY PROGRAM
4555.
45111
43771
43377
.6322
66622
.223.
42233
45553
45111
46771
66677
4555.
45111
43771
43377
2236.
22666
4555.
45111
42231
42233
.6773
66677
.1114
22314
22334
55534
5677.
66677
.111.
22314
22334
55534
56774
66677
*/