diff --git a/client/board.js b/client/board.js index cfcac25..beceed7 100644 --- a/client/board.js +++ b/client/board.js @@ -13,14 +13,14 @@ Board.prototype.drawRobots = function(robots) { this.robots = {}; robots.forEach(({ color, i, j }) => { - this.robots[`${i}-${j}`] = true; - const { x, y } = this.squares.ijToXy({ i, j }); const s = this.squares.sideLength; const id = color.replace('#', '').toUpperCase(); + const ij = `${i}-${j}`; + this.robots[ij] = id; - // Get robot from ij: document.querySelector('[data-robot=i-j]') const robot = document.createElement('div'); + robot.id = `robot-${id}`; robot.className = 'content-robot'; robot.style.background = `radial-gradient(circle at ${s/3}px ${s/3}px, ${color} 10%, #000)`; robot.style.borderRadius = (s / 2) + 'px'; @@ -28,87 +28,64 @@ Board.prototype.drawRobots = function(robots) { robot.style.width = s + 'px'; robot.style.left = x + 'px'; robot.style.top = y + 'px'; - robot.id = id; - robot.dataset.robot = `${i}-${j}`; - - const shadow = document.createElement('div'); - shadow.className = 'content-shadow'; - shadow.style.background = `radial-gradient(circle at ${s/3}px ${s/3}px, ${color} 10%, #000)`; - shadow.style.borderRadius = (s / 2) + 'px'; - shadow.style.height = s + 'px'; - shadow.style.width = s + 'px'; - shadow.style.left = x + 'px'; - shadow.style.top = y + 'px'; - shadow.id = `shadow-${id}`; - shadow.dataset.parentRobot = id; - shadow.dataset.currentIJ = `${i}-${j}`; - - const up = document.createElement('div'); - up.className = 'content-arrow'; - up.innerHTML = "▲" - up.style.left = x + 'px'; - up.style.top = (y - s) + 'px'; - up.style.lineHeight = s + 'px'; - up.style.height = s + 'px'; - up.style.width = s + 'px'; - up.dataset.i = i; - up.dataset.j = j; - up.dataset.direction = 'up'; - up.dataset.parentRobot = id; - up.addEventListener('click', this.moveRobot.bind(this)); - - const down = document.createElement('div'); - down.className = 'content-arrow'; - down.innerHTML = "▼" - down.style.left = x + 'px'; - down.style.top = (y + s) + 'px'; - down.style.lineHeight = s + 'px'; - down.style.height = s + 'px'; - down.style.width = s + 'px'; - down.dataset.i = i; - down.dataset.j = j; - down.dataset.direction = 'down'; - down.dataset.parentRobot = id; - down.addEventListener('click', this.moveRobot.bind(this)); - - const left = document.createElement('div'); - left.className = 'content-arrow'; - left.innerHTML = "◀" - left.style.left = (x - s) + 'px'; - left.style.top = y + 'px'; - left.style.lineHeight = s + 'px'; - left.style.height = s + 'px'; - left.style.width = s + 'px'; - left.dataset.i = i; - left.dataset.j = j; - left.dataset.direction = 'left'; - left.dataset.parentRobot = id; - left.addEventListener('click', this.moveRobot.bind(this)); + robot.dataset.i = i; + robot.dataset.j = j; + + // Get robot from ij: document.querySelector('[data-robot=i-j]') + // robot.dataset.robot = ij; + + const arrows = document.createElement('div'); + arrows.className = 'content-arrows'; + arrows.id = `arrows-${id}`; + arrows.style.position = 'absolute'; + arrows.style.left = (x - s) + 'px'; + arrows.style.top = (y - s) + 'px'; - const right = document.createElement('div'); - right.className = 'content-arrow'; - right.innerHTML = "▶" - right.style.left = (x + s) + 'px'; - right.style.top = y + 'px'; - right.style.lineHeight = s + 'px'; - right.style.height = s + 'px'; - right.style.width = s + 'px'; - right.dataset.i = i; - right.dataset.j = j; - right.dataset.direction = 'right'; - right.dataset.parentRobot = id; - right.addEventListener('click', this.moveRobot.bind(this)); - - - // this.parent.appendChild(robot); - this.parent.appendChild(shadow); - this.parent.appendChild(up); - this.parent.appendChild(down); - this.parent.appendChild(left); - this.parent.appendChild(right); - - // shadow.addEventListener('mousedown', this.onRobotDragStart.bind(this)); + const up = this.drawArrow({ + direction: 'up', label: '▲', i, j, left: s, top: 0, parentId: id + }); + + const down = this.drawArrow({ + direction: 'down', label: '▼', i, j, left: s, top: (2 * s), parentId: id + }); + + const left = this.drawArrow({ + direction: 'left', label: '◀', i, j, left: 0, top: s, parentId: id + }); + + const right = this.drawArrow({ + direction: 'right', label: '▶', i, j, left: (2 * s), top: s, parentId: id + }); + + arrows.appendChild(up); + arrows.appendChild(down); + arrows.appendChild(left); + arrows.appendChild(right); + + this.parent.appendChild(robot); + this.parent.appendChild(arrows); }); + + this.updateArrowVisibilities(); +}; + +Board.prototype.drawArrow = function({ direction, label, i, j, left, top, parentId }) { + const s = this.squares.sideLength; + + const arrow = document.createElement('div'); + arrow.className = 'content-arrow'; + arrow.innerHTML = label; + arrow.style.left = left + 'px'; + arrow.style.top = top + 'px'; + arrow.style.lineHeight = s + 'px'; + arrow.style.height = s + 'px'; + arrow.style.width = s + 'px'; + arrow.dataset.direction = direction; + arrow.dataset.parent = parentId; + + arrow.addEventListener('click', this.onArrowClick.bind(this)); + + return arrow; }; Board.prototype.drawSquares = function() { @@ -163,21 +140,71 @@ Board.prototype.drawWalls = function(edges) { } this.parent.appendChild(wall) - }) + }); + + this.updateArrowVisibilities(); }; -Board.prototype.moveRobot = function(evt) { - const i = evt.currentTarget.dataset.i * 1; - const j = evt.currentTarget.dataset.j * 1; +Board.prototype.moveRobot = function({ id, i, j }) { + const robot = document.getElementById(`robot-${id}`); + const arrows = document.getElementById(`arrows-${id}`); + + const { x, y } = this.squares.ijToXy({ i, j }); + const s = this.squares.sideLength; + + robot.style.left = x + 'px'; + robot.style.top = y + 'px'; + robot.dataset.i = i; + robot.dataset.j = j; + + this.robots[`${i}-${j}`] = id; + + arrows.style.left = (x - s) + 'px'; + arrows.style.top = (y - s) + 'px'; + + this.updateArrowVisibilities(); +} + +Board.prototype.updateArrowVisibilities = function() { + const keys = Object.keys(this.robots); + + keys.forEach(key => { + const id = this.robots[key]; + const i = key.split('-')[0] * 1; + const j = key.split('-')[1] * 1; + + const arrows = document.getElementById(`arrows-${id}`); + + const ijR = `${i + 1}-${j}`; + const ijL = `${i - 1}-${j}`; + const ijU = `${i}-${j - 1}`; + const ijD = `${i}-${j + 1}`; + + const edgeR = `${i + 1}-${j}-${i + 1}-${j + 1}`; + const edgeL = `${i}-${j}-${i}-${j + 1}`; + const edgeU = `${i}-${j}-${i + 1}-${j}`; + const edgeD = `${i}-${j + 1}-${i + 1}-${j + 1}`; + + arrows.querySelector("[data-direction='right']").style.display = (this.robots[ijR] || this.walls[edgeR] || i === (this.squares.perSide - 1)) ? 'none' : 'block'; + arrows.querySelector("[data-direction='left']").style.display = (this.robots[ijL] || this.walls[edgeL] || i === 0) ? 'none' : 'block'; + arrows.querySelector("[data-direction='up']").style.display = (this.robots[ijU] || this.walls[edgeU] || j === 0) ? 'none' : 'block'; + arrows.querySelector("[data-direction='down']").style.display = (this.robots[ijD] || this.walls[edgeD] || j === (this.squares.perSide - 1)) ? 'none' : 'block'; + }); +} + +Board.prototype.onArrowClick = function(evt) { const direction = evt.currentTarget.dataset.direction; - const id = evt.currentTarget.dataset.parentRobot; + const id = evt.currentTarget.dataset.parent; + + const robot = document.getElementById(`robot-${id}`); + const i = robot.dataset.i * 1; + const j = robot.dataset.j * 1; + + delete this.robots[`${i}-${j}`]; const { i: i2, j: j2 } = this.findNextObstacle({ direction, i, j }); - const {x, y} = this.squares.ijToXy({ i: i2, j: j2 }); - const robot = document.getElementById(`shadow-${id}`); - robot.style.left = x + 'px' - robot.style.top = y + 'px' + this.moveRobot({ id, i: i2, j: j2 }) }; Board.prototype.findNextObstacle = function({ direction, i, j }) { @@ -185,9 +212,9 @@ Board.prototype.findNextObstacle = function({ direction, i, j }) { case 'right': for (let ii = i + 1; ii < this.squares.perSide; ii++) { const edge = `${ii + 1}-${j}-${ii + 1}-${j + 1}`; - const ij = `${ii}-${j}`; + const ij = `${ii + 1}-${j}`; - if (this.robots[ij] || this.walls[edge]) { + if (this.robots[ij] || this.walls[edge] || ii === (this.squares.perSide - 1)) { return { i: ii, j }; } } @@ -195,9 +222,9 @@ Board.prototype.findNextObstacle = function({ direction, i, j }) { case 'left': for (let ii = i - 1; ii >= 0; ii--) { const edge = `${ii}-${j}-${ii}-${j + 1}`; - const ij = `${ii}-${j}`; + const ij = `${ii - 1}-${j}`; - if (this.robots[ij] || this.walls[edge]) { + if (this.robots[ij] || this.walls[edge] || ii === 0) { return { i: ii, j }; } } @@ -206,9 +233,9 @@ Board.prototype.findNextObstacle = function({ direction, i, j }) { case 'up': for (let jj = j - 1; jj >= 0; jj--) { const edge = `${i}-${jj}-${i + 1}-${jj}`; - const ij = `${i}-${jj}`; + const ij = `${i}-${jj - 1}`; - if (this.robots[ij] || this.walls[edge]) { + if (this.robots[ij] || this.walls[edge] || jj === 0) { return { i, j: jj }; } } @@ -217,9 +244,9 @@ Board.prototype.findNextObstacle = function({ direction, i, j }) { case 'down': for (let jj = j + 1; jj < this.squares.perSide; jj++) { const edge = `${i}-${jj + 1}-${i + 1}-${jj + 1}`; - const ij = `${i}-${jj}`; + const ij = `${i}-${jj + 1}`; - if (this.robots[ij] || this.walls[edge]) { + if (this.robots[ij] || this.walls[edge] || jj === (this.squares.perSide - 1)) { return { i, j: jj }; } } @@ -228,112 +255,3 @@ Board.prototype.findNextObstacle = function({ direction, i, j }) { throw Error("Could not find next obstacle, no direction found. ", direction, i, j); } - -// i1 and j1 are the original position. -// i2 and j2 are the requested position. -// Check all the edges crossed between the two to find if it's a valid move. -// Board.prototype.getBlockers = function({ i1, j1, i2, j2 }) { -// if (i1 !== i2 && j1 !== j2) { -// return "diagonal move"; -// } - -// if (i1 === i2 && j1 == j2) { -// return; -// } else if (i1 === i2 && j1 > j2) { -// // Moving up. -// for (let j = j2; j < j1; j++) { -// const edge = `${i1}-${j + 1}-${i1 + 1}-${j + 1}`; -// const ij = `${i1}-${j}`; - -// if (this.robots[ij]) { -// return document.querySelector(`[data-robot='${ij}']`); -// } - -// if (this.walls[edge]) { -// return document.querySelector(`[data-wall='${edge}']`); -// } -// } - -// } else if (i1 === i2 && j1 < j2) { -// // Moving down. -// for (let j = j1; j < j2; j++) { -// const edge = `${i1}-${j + 1}-${i1 + 1}-${j + 1}`; -// const ij = `${i1}-${j + 1}`; - -// if (this.robots[ij]) { -// return document.querySelector(`[data-robot='${ij}']`); -// } - -// if (this.walls[edge]) { -// return document.querySelector(`[data-wall='${edge}']`); -// } -// } -// } else if (j1 === j2 && i1 < i2) { -// // Moving right. -// for (let i = i1; i < i2; i++) { -// const edge = `${i + 1}-${j1}-${i + 1}-${j1 + 1}`; -// const ij = `${i + 1}-${j1}`; - -// if (this.robots[ij]) { -// return document.querySelector(`[data-robot='${ij}']`); -// } - -// if (this.walls[edge]) { -// return document.querySelector(`[data-wall='${edge}']`); -// } -// } -// } else { -// // Moving left. -// for (let i = i2; i < i1; i++) { -// const edge = `${i + 1}-${j1}-${i + 1}-${j1 + 1}`; -// const ij = `${i}-${j1}`; - -// if (this.robots[ij]) { -// return document.querySelector(`[data-robot='${ij}']`); -// } - -// if (this.walls[edge]) { -// return document.querySelector(`[data-wall='${edge}']`); -// } -// } -// } -// }; - -// Board.prototype.onRobotDragStart = function(evt) { -// evt.stopPropagation(); -// evt.preventDefault(); - -// this.listeners.onRobotDragStop = this.onRobotDragStop.bind(this); -// this.listeners.onRobotDrag = this.onRobotDrag.bind(this, evt.currentTarget); - -// document.body.addEventListener('mouseup', this.listeners.onRobotDragStop); -// document.body.addEventListener('mousemove', this.listeners.onRobotDrag); - -// const { x, y } = this.squares.ijToXy(this.squares.xyToIj(evt)); - -// evt.currentTarget.style.left = x + 'px'; -// evt.currentTarget.style.top = y + 'px'; -// }; - -// Board.prototype.onRobotDrag = function(dragTarget, evt) { -// const [ i1, j1 ] = dragTarget.dataset.currentIJ.split('-').map(v => v * 1); -// // cost { i: i1, j: j1 } = this.squares.xyToIj({ x: evt.x - evt.movementX, y: evt.y - evt.movementY }); -// const { i: i2, j: j2 } = this.squares.xyToIj({ x: evt.x, y: evt.y }); - -// const blockage = this.getBlockers({ i1, j1, i2, j2 }); - -// if (blockage) { -// // console.log(blockage) -// return; -// } - -// const { x, y } = this.squares.ijToXy({ i: i2, j: j2 }); - -// dragTarget.style.left = x + 'px'; -// dragTarget.style.top = y + 'px'; -// }; - -// Board.prototype.onRobotDragStop = function(_) { -// document.body.removeEventListener('mouseup', this.listeners.onRobotDragStop); -// document.body.removeEventListener('mousemove', this.listeners.onRobotDrag); -// }; \ No newline at end of file diff --git a/index.css b/index.css index 1cb372f..1f55b88 100644 --- a/index.css +++ b/index.css @@ -121,12 +121,11 @@ body { } .content-robot { - opacity: 0.25; position: absolute; + transition: left 0.4s cubic-bezier(0,1,.5,1), top 0.4s cubic-bezier(0,1,.5,1); } -.content-shadow { - position: absolute; +.content-arrows { transition: left 0.4s cubic-bezier(0,1,.5,1), top 0.4s cubic-bezier(0,1,.5,1); } @@ -135,6 +134,7 @@ body { cursor: pointer; position: absolute; text-align: center; + user-select: none; } .content-arrow:hover { diff --git a/server/game.js b/server/game.js index e55a3f8..2cf9e48 100644 --- a/server/game.js +++ b/server/game.js @@ -23,19 +23,21 @@ Game.prototype.getPlayers = function() { Game.prototype.getRobots = function() { return [ - {i: 4, j: 3, color: '#E00000' }, - {i: 4, j: 7, color: '#00C000' }, - {i: 1, j: 19, color: '#0000FF' } + {i: 9, j: 9, color: '#E00000' }, + {i: 18, j: 9, color: '#00C000' }, + {i: 1, j: 9, color: '#0000FF' }, + {i: 9, j: 18, color: '#00C0C0' }, + {i: 9, j: 1, color: '#F000F0' }, ]; } Game.prototype.getWalls = function() { // Edge IDs are of the form [i1-j1-i2-j2]. Top left is 0, 0. return [ - "1-3-1-4", - "4-1-5-1", - "4-8-5-8", - "8-3-8-4" + "1-9-1-10", + "9-1-10-1", + "9-19-10-19", + "19-9-19-10" ]; };