I have a problem with a puzzle named "A child's play" of Codingame. I code in typescript !
The statement is :
For several years now, in elementary schools, we have seen the emergence of a new educational model, playful programming. The students must program a small robot using assembly blocks. This allows them to get familiar with programming from an early age while exercising their logic and perception of space.
You are a student at one such school. The purpose of the exercise is simple: your teacher has crafted a circuit for your robot, told you how many moves n the robot may make, and you must find out the final position of the robot at end of execution.
To do this you need to know some principles of robot operation.
– When the robot encounters an obstacle (represented by #) it turns right (on same operation) until there's no obstacle ahead anymore. Otherwise on an empty area (represented by .) it moves straight ahead.
– The robot initially moves upwards.
– The robot stops after n moves.
– The top left corner represents the coordinates (0,0)
– The robot's environment is represented as follows, where O is the robot's initial position:
...#........
...........#
............
............
..#O........
..........#.
I'm block at the test 4, because my solution is not optimized.
It gives me in the console: The process timed out. This may mean that your solution is not optimized enough to handle some cases.
I tried to change the loop for a "while" loop and change and change the "switch" condition, for an "if-else" condition.
Can you help me to find a better solution or a other way to do the tests?
var inputs: string[] = readline().split(' ');
const w: number = parseInt(inputs[0]);
const h: number = parseInt(inputs[1]);
const n: number = parseInt(readline());
let zone: string[][] = [];
let robot: robot = { x: 0, y: 0 };
interface robot {
x: number,
y: number
}
for (let i = 0; i < h; i++) {
const line: string = readline();
zone = [...zone, line.split('')]
}
zone.forEach((line, y) => {
line.forEach((place, x) => {
if (place === "O") {
robot.x = x;
robot.y = y;
}
})
})
function getLoc(robot: robot, zone: string[][], tours: number) {
let direct: string = "T";
var i = 0;
while (i < tours) {
if (direct === "T") {
if (zone[robot.y - 1][robot.x] === '#') {
robot.x++;
direct = "R";
} else {
robot.y--;
}
} else if (direct === "R") {
if (zone[robot.y][robot.x + 1] === '#') {
robot.y++;
direct = "B";
} else {
robot.x++;
}
} else if (direct === "B") {
if (zone[robot.y + 1][robot.x] === '#') {
robot.x--;
direct = "L";
} else {
robot.y++;
}
} else if (direct === "L") {
if (zone[robot.y][robot.x - 1] === '#') {
robot.y--;
direct = "T";
} else {
robot.x--;
}
}
i++;
}
return robot
}
console.time("getLoc")
let res: robot = getLoc(robot, zone, n);
console.timeEnd("getLoc")
console.log(`${res.x} ${res.y}`)
The robot will go into a loop.
You have to figure out how long the loop is, then how many times it goes through it, then complete the last partial loop.
I recommend keeping an object saying when it was last at a given position and orientation. When you find that it repeats, you now know the length of the loop and can jump over the repetitive behavior.
thanks for your tips, it was helped me for the resolution of the problem ! I did change the structure of main code, I did add three functions, getSteps() to get remaining moves and detect if there is a loop, moveRobot() to move the robot and get all moves. getLoc() to get the final position with the resultat of getSteps(). I reach the 8nth test, but i'm blocked at the 9nth test. The answer is "1 3", but I get "2 4" ! I don't know how to solve this problem without break the rest.
The map is :
#####
#...#
#.#.#
#...#
##O##
The code is :
var inputs: string[] = readline().split(' ');
const w: number = parseInt(inputs[0]);
const h: number = parseInt(inputs[1]);
const n: number = parseInt(readline());
let zone: string[][] = [];
let robot: robot = { x: 0, y: 0 };
let startRobot: startRobot = { x: 0, y: 0, loops: 1, steps: 0 };
interface startRobot extends robot {
loops: number,
steps: number
}
interface robot {
x: number,
y: number
}
for (let i = 0; i < h; i++) {
const line: string = readline();
zone = [...zone, line.split('')]
console.error(line)
}
zone.forEach((line, y) => {
line.forEach((place, x) => {
if (place === "O") {
robot.x = x;
robot.y = y;
startRobot.x = x;
startRobot.y = y;
}
})
})
function getSteps(robot: robot, zone: string[][], steps: number, start: startRobot) {
let direct: string = "T";
var i: number = 0;
var s: number = 0;
let quotient: number = 0;
let newSteps: number = 0;
while (i < steps) {
let resfunction = moveRobot(direct, zone, robot);
robot = resfunction.robot;
direct = resfunction.direct;
i++;
start.steps++;
if (robot.x === start.x && robot.y === start.y) {
s++;
}
if (s === start.loops) {
quotient = steps / start.steps;
newSteps = steps - start.steps * (Math.floor(quotient) - 1);
break;
}
}
return newSteps
}
function moveRobot(direct: string, zone: string[][], robot: robot) {
if (direct === "T") {
if (robot.y - 1 >= zone.length) {
if (zone[robot.y][robot.x] === '#') {
robot.x++;
direct = "R";
} else {
robot.y--;
}
} else {
if (zone[robot.y - 1][robot.x] === '#') {
robot.x++;
direct = "R";
} else {
robot.y--;
}
}
} else if (direct === "R") {
if (robot.x + 1 >= zone[0].length) {
if (zone[robot.y][robot.x] === '#') {
robot.y++;
direct = "B";
} else {
robot.x++;
}
} else {
if (zone[robot.y][robot.x + 1] === '#') {
robot.y++;
direct = "B";
} else {
robot.x++;
}
}
} else if (direct === "B") {
if (robot.y + 1 >= zone.length) {
if (zone[robot.y][robot.x] === '#') {
robot.x--;
direct = "L";
} else {
robot.y++;
}
} else {
if (zone[robot.y + 1][robot.x] === '#') {
robot.x--;
direct = "L";
} else {
robot.y++;
}
}
} else if (direct === "L") {
if (robot.x - 1 >= zone[0].length) {
if (zone[robot.y][robot.x] === '#') {
robot.y--;
direct = "T";
} else {
robot.x--;
}
} else {
if (zone[robot.y][robot.x - 1] === '#') {
robot.y--;
direct = "T";
} else {
robot.x--;
}
}
}
return { direct, robot }
}
function getLoc(robot: robot, zone: string[][], steps: number, start: startRobot) {
let direct: string = "T";
var i: number = 0;
while (i < steps) {
let resfunction = moveRobot(direct, zone, robot);
robot = resfunction.robot;
direct = resfunction.direct;
i++;
}
return robot
}
// console.time("getLoc")
let newSteps: number = getSteps(robot, zone, n, startRobot);
let res: robot = getLoc(robot, zone, newSteps, startRobot);
// console.timeEnd("getLoc")
console.log(`${res.x} ${res.y}`)
Related
I need to insert a lot of coordinates in a binary tree and sometimes merge them. I'm testing the program for a while and have noted that the search function doesn't work sometimes.
In this example, I created two binary trees, merged them and searched for some coordinate, but the coordinates that are inserted in the tree, were not found:
First tree:
0 2
9 8
7 0
6 0
Second tree:
3 2
8 5
4 1
5 6
Merged trees:
3 2
8 5
4 1
5 6
0 2
9 8
7 0
6 0
VALUES 8 and 5 NOT FOUND
Can someone help me with this problem?
Here is the code:
function binarytree()
{
this.root = null;
this.add = function()
{
var node = {
x : j,
y : i,
left : null,
right : null
};
var current;
if (this.root == null) this.root = node;
else {
current = this.root;
while (1)
{
if (i < current.y || j < current.x) {
if (current.left == null) {
current.left = node;
break;
}
else current = current.left;
}
else if (i > current.y || j > current.x) {
if (current.right == null) {
current.right = node;
break;
}
else current = current.right;
}
else break;
}
}
}
this.search = function(tree, i, j) {
var found = false;
current = tree.root;
while (!found && current) {
if (i < current.y || j < current.x) current = current.left;
else if (i > current.y || j > current.x) current = current.right;
else found = true;
}
return found;
}
this.print = function(no)
{
if (no)
{
this.print(no.left);
this.print(no.right);
console.log(no.x, no.y);
}
}
}
function merge(tree, tree2) {
if (tree2.x < tree.x || tree2.y < tree.y) {
if (tree.left) {
this.merge(tree.left, tree2);
} else {
tree.left = tree2;
}
} else {
if (tree.right) {
this.merge(tree.right, tree2);
} else {
tree.right = tree2;
}
}
}
var i, j;
var tree = new binarytree();
var tree2 = new binarytree();
for (x = 0; x < 4; x++)
{
i = Math.floor(Math.random() * 10);
j = Math.floor(Math.random() * 10);
tree.add();
}
for (x = 0; x < 4; x++)
{
i = Math.floor(Math.random() * 10);
j = Math.floor(Math.random() * 10);
tree2.add();
}
console.log("First tree:");
tree.print(tree.root);
console.log("Second tree:");
tree2.print(tree2.root);
merge(tree.root,tree2.root);
console.log("Merged trees:");
tree.print(tree.root);
if (tree.search(tree, i, j) == true) console.log("FOUND VALUES " + j + " AND " + i);
else console.log("VALUES " + j + " AND " + i + " NOT FOUND");
As you already mentioned in your deleted question, you need to use KD trees for this, as a normal binary search tree is intended for 1-dimensional values, not 2-dimensional values.
There are several issues in your code:
The way you split points into 2 categories is not a transitive operation. Let's say you have two points in one tree: (0, 0) and (10, -5), then the second point will be stored in the left property of the root. This is because -5 is less than 0. Now we have a second tree with two points: (4, 4) and also (10, -5). The tree will have the same structure as the first for the same reasons. Your merge function will put the second tree in the right property of the first tree's root. This is because (4, 4) is considered "right" of (0, 0). Now notice how this is inconsistent: now we have a (10, -5) sitting both in the left and the right subtree of the merged tree! This happens because the way you compare points is not a transitive comparison.
The above point is the major problem, but also notice that if (i < current.y || j < current.x) will on average be true for 75% of the cases. This is a second reason why this comparison method is not the right one.
The way to compare 2D-points in a KD tree is to alternate comparisons with either the X-coordinate or the Y-coordinate. So at the top-level of the tree you would compare Y-coordinates to decide whether to go left or right, and on the next level you would compare X-coordinates. Then again one level deeper you would compare Y-coordinates again, ...etc.
Some other remarks:
Use class syntax
Create a constructor also for the node instances
Don't print in methods, instead define a generator to produce all values, and a toString method and print that string in your main program.
Avoid using global variables: your add method should take i and j as arguments, and you should declare variables always (you didn't use var current in search, nor for x and y in the main code): this is crucial if you want to write reliable code. Consider using "use strict" which will alert you about such problems.
add and search will use a similar way to navigate through the tree, so put the code that they have in common in a separate function, which we could call locate.
As far as I know, there is no way to merge KD trees the way you have imagined it, where you can decide to put the "rest" of the second tree under a leaf node of the first. So I would suggest to just iterate the nodes of the second tree and insert them one by one in the first.
Here is the suggested code:
"use strict"; // To help avoid all kinds of problems
class Node { // Use a class for creating node instances
constructor(i, j) {
this.x = i;
this.y = j;
this.left = null;
this.right = null;
}
* inorder() { // Generator to visit all nodes
if (this.left) yield * this.left.inorder();
yield [this.x, this.y];
if (this.right) yield * this.right.inorder();
}
}
class BinaryTree {
constructor() {
this.root = null;
}
locate(i, j) { // A common function to avoid code repetition
if (this.root == null) return [null, "root"];
let current = this.root;
let axis = "x";
let otherAxis = "y";
while (true) {
// alternate the axis:
[axis, otherAxis, i, j] = [otherAxis, axis, j, i];
if (i < current[axis]) {
if (current.left == null) return [current, "left"];
current = current.left;
} else if (i > current[axis] || j != current[otherAxis]) {
if (current.right == null) return [current, "right"];
current = current.right;
} else { // point is already in the tree
return [current, "equal"];
}
}
}
add(i, j) { // this method should have parameters
if (this.root == null) {
this.root = new Node(i, j); // Use constructor for creating node
} else {
const [current, side] = this.locate(i, j);
if (side !== "equal") {
current[side] = new Node(i, j);
}
}
}
search(i, j) {
const [_, side] = this.locate(i, j);
return side === "equal";
}
* inorder() {
if (this.root) yield * this.root.inorder();
}
mergeWith(otherTree) {
// Insert all the other nodes one by one:
for (let point of otherTree.inorder()) {
this.add(...point);
}
}
toString() { // Don't print, but produce string
return JSON.stringify(Array.from(this.inorder()));
}
}
const tree = new BinaryTree();
for (const point of [[0, 2], [9, 8], [7, 0], [6, 0]]) tree.add(...point);
const tree2 = new BinaryTree();
for (const point of [[3, 2], [8, 5], [4, 1], [5, 6]]) tree2.add(...point);
console.log("First tree:");
console.log(tree.toString());
console.log("Second tree:");
console.log(tree2.toString());
tree.mergeWith(tree2);
console.log("Merged trees:");
console.log(tree.toString());
// Check that all points are found:
for (const point of tree.inorder()) {
console.log(...point, tree.search(...point));
}
I've been trying to resolve the following problem:
You live in the city of Cartesia where all roads are laid out in a perfect grid. You arrived ten minutes too early to an appointment, so you decided to take the opportunity to go for a short walk. The city provides its citizens with a Walk Generating App on their phones -- everytime you press the button it sends you an array of one-letter strings representing directions to walk (eg. ['n', 's', 'w', 'e']). You always walk only a single block for each letter (direction) and you know it takes you one minute to traverse one city block, so create a function that will return true if the walk the app gives you will take you exactly ten minutes (you don't want to be early or late!) and will, of course, return you to your starting point. Return false otherwise.
Note: you will always receive a valid array containing a random assortment of direction letters ('n', 's', 'e', or 'w' only). It will never give you an empty array (that's not a walk, that's standing still!).
Here is the code I wrote:
function walkTime(walk) {
//insert brilliant code here
var walkLength = walk.length;
if (walkLength === 10) {
return true;
}
else {
return false;
}
}
findEndPosition = (Arr) => {
var y = 0
if (Arr.length > 10) {
return false
}
else {
Arr.forEach((x) => {
if (x === 'n') {
y++;
}
if (x === 's') {
y--;
}
if (x === 'e') {
y++;
}
if (x === 'w') {
y--;
}
})
};
if (y === 0) {
return true;
}
else {
return false;
}
}
const isValidWalk = (walk) => {
if(walkTime(walk) === true && findEndPosition(walk) === true){
return true
}
else {
return false
}
}
console.log(isValidWalk(['w','e','w','e','w','e','w','e','w','e','w','e']));
I keep trying to passing all the tests except for two. Unfortunately it's not telling me what inputs it's using that keep failing :/. Anyone think they know what's going on, this is driving me crazy! Thanks in advance!
If your goal is only to check if the walk will take exactly 10 minutes, it should be enough to test that the walk array is 10 in length and the walk steps are valid. Isn't it so?
In your code, you have a function for solving the end position. There is a clear mistake as steps to west and east are changing the y variable. Is your actual problem more related to this one?
Just wanted to let you guys know I found this solution. It worked through all the tests! I'm sure it could be written a lot better, if you guys got suggestions I'd appreciate it! Anyways here it is!
function walkTime(walk) {
//insert brilliant code here
var walkLength = walk.length;
if (walkLength === 10) {
return true;
}
else {
return false;
}
}
const findEndPosition = (Arr) => {
var y = 0;
var x = 0;
if (Arr.length > 10) {
return false
}
else {
Arr.forEach((movement) => {
if (movement === 'n') {
y++;
}
if (movement === 's') {
y--;
}
if (movement === 'e') {
x++;
}
if (movement === 'w') {
x--;
}
})
};
const newLocation = [x,y];
if (newLocation.toString() === '0,0') {
return true;
}
else {
return false;
}
}
const isValidWalk = (walk) => {
if(walkTime(walk) === true && findEndPosition(walk) === true){
return true
}
else {
return false
}
}
console.log(isValidWalk(['n','s','e','w','n','s','e','w','e','w']));
I'm learning algorithms and doing JavaScript exercise questions, and I don't understand how one reaches the correct answer for a particular algorithm.
The question provided in the exercise is:
Have the function CorrectPath(str) read the str parameter being
passed, which will represent the movements made in a 5x5 grid of cells
starting from the top left position. The characters in the input
string will be entirely composed of: r, l, u, d, ?. Each of the
characters stand for the direction to take within the grid, for
example: r = right, l = left, u = up, d = down. Your goal is to
determine what characters the question marks should be in order for a
path to be created to go from the top left of the grid all the way to
the bottom right without touching previously travelled on cells in the
grid.
For example, the input drdr??rrddd? should ouptut drdruurrdddd
I've not found a solution on my own. I'm taking a look at a solution provided, and I'm bothered because:
A. pure functions are not used to manipulate values within the CorrectPath function (note the addX() and addY() methods contained within). I'm not convinced the solution provided is using best practices, especially coming from a functional programming background.
B. I don't understand how the steps taken, specifically in the while block and the succeeding for block, are taken to reach the correct answer and why sometimes the missingLetters array has letters remaining and other times not
The working solution provided is below
function CorrectPath(str) {
let x = 0, //start x coord
y = 0, //start y coord
missingLetters = []
const unknowns = str.match(/\W/g)
function addX() {
while(x !== 4) {
if (x > 4) {
x--;
missingLetters.push('l');
} else {
x++;
missingLetters.push('r');
}
}
}
function addY() {
while (y !== 4) {
if (y > 4) {
y--;
missingLetters.push('u');
} else {
y++;
missingLetters.push('d');
}
}
}
//tallies current number of x and y movements
for (let i=0; i<str.length; i++) {
switch (str[i]) {
case 'd':
y += 1;
break;
case 'u':
y -= 1;
break;
case 'l':
x -= 1;
break;
case 'r':
x += 1;
break;
}
}
if (x > y) { addX(); addY(); }
if (y >= x) { addY(); addX(); }
while (missingLetters.length < unknowns.length) {
var pos = missingLetters.length - 1;
if (missingLetters[pos] === 'r') {x += 1; missingLetters.push('r'); addX()}
if (missingLetters[pos] === 'l') {x -= 1; missingLetters.push('l'); addX()}
if (missingLetters[pos] === 'd') {y += 1; missingLetters.push('d'); addY()}
if (missingLetters[pos] === 'u') {y -= 1; missingLetters.push('u'); addY()}
}
var newStr = str.split('');
for (var j=0; j<str.length; j++) {
if (newStr[j] === '?') {
newStr[j] = missingLetters.shift()
}
}
return newStr.join('');
}
CorrectPath(readline());
Here's a solution I found
const dirMap = {
u: { x: 0, y: -1 },
r: { x: 1, y: 0 },
d: { x: 0, y: 1 },
l: { x: -1, y: 0 }
}
function CorrectPath(pathString) {
const map = Array(5*5)
return trace(pathString, map)
}
function trace(path, [...map], x = 0, y = 0, newPath = "") {
const steps = path.split(""),
nextMove = steps.shift()
if (nextMove === undefined) {
if (5 * y + x === (5*5-1)) return newPath
return "Bad move"
}
if (nextMove === "?") {
const moves = availableMoves(x,y,map)
if (!moves.length) return "Bad move"
for(let i = 0; i<moves.length; i++) {
let move = moves[i],
trySteps = [move,...steps].join("")
res = trace(trySteps,map,x,y,newPath)
if (!res || res === "Bad move") continue
else return res
}
return "Bad move"
} else {
if (!canMove(nextMove, x, y, map)) return "Bad move"
const pos = dirMap[nextMove],
newX = pos.x + x,
newY = pos.y + y
newPath += nextMove
map[5*newY+newX] = nextMove
return trace(steps.join(""),map,newX,newY,newPath)
}
}
function availableMoves(x,y,map) {
const steps = []
Object.keys(dirMap).forEach(z => {
if (canMove(z,x,y,map)) steps.push(z)
})
return steps
}
function canMove(dir, xPath, yPath, map) {
const pos = dirMap[dir],
x = pos.x + xPath,
y = pos.y + yPath
if (x > 4 || x < 0 || y > 4 || y < 0) return false
if (map[5*y+x] !== undefined) return false
return true
}
CorrectPath(readline());
This is the Javascript code for my Tic-Tac-Toe algorithm:
function minimax(newGrid, depth, Player){
//debugger
const gameState = checkWin(newGrid,true);
if (gameState == false){
values = [];
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
const boardCopy = _.cloneDeep(newGrid);
if (boardCopy[i][j] !== '') continue;
boardCopy[i][j] = Player;
console.log(boardCopy);
const value = minimax(boardCopy, depth + 1, (Player === PLYR_TOKEN) ? COMP_TOKEN : PLYR_TOKEN);
values.push({
cost: value,
cell: {
i: i,
j: j
}
});
}
}
//debugger
if (Player === COMP_TOKEN){
const max = _.maxBy(values, (v) => {
return v.cost;
});
if( depth === 0 ){
return max.cell;
} else {
return max.cost;
}
}else{
const min = _.minBy(values, (v) => {
return v.cost;
});
if( depth === 0 ){
return min.cell;
} else {
return min.cost;
}
}
} else if (gameState === null){
return 0;
} else if (gameState === PLYR_TOKEN){
return depth - 10;
} else if (gameState === COMP_TOKEN){
return 10 - depth;
}
}
The problem with this "algorithm", "code" is simple: it doesn't block my moves Let's image this scenario:
X-> player
O-> MM algorithm
X - O
- X -
- - -
Normal, a perfect MiniMax algorithm should take this choice to block me from winning (lower case o is the new move):
X - O
- X -
- - o
The problem is that my code dose this (lower case o is the new move):
X - O
- X o
- - -
Why? I dont know, but I think it take's any chance it has to win, ignoring my moves, ignoring the fact that I'm one move away from winning.
To be honest with you, I don't really understand how this algorithm work's.
Other info: The main board is an two dimensional array and the result of the minimax function is an object with two proprieties (i,j) that represent coordinates on the main board.
const board = [
['','',''],
['','',''],
['','','']
];
So, when in doubt, comment ! I did it step by step, not stopping when I was stuck but delaying and going back and forth each time I understood something more.
//newGrid : the board
//depth : keep track of the "guessing level"
//Player : keep track of who's turn it would be
function minimax(newGrid, depth, Player){
//checking if the game ended
const gameState = checkWin(newGrid,true);
//if not
if (gameState == false){
values = [];
//for each cell in the grid
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
//we make a deep copy of the board
const boardCopy = _.cloneDeep(newGrid);
//if the cell isn't empty, we jump to the next one
if (boardCopy[i][j] !== '') continue;
//here we assign the Player to the cell (simulating a move from this player to this cell)
boardCopy[i][j] = Player;
//debugging
console.log(boardCopy);
//here go some recursivity, so we're putting our deepcopy with the simulated move, adding a depth level, and switching player
const value = minimax(boardCopy, depth + 1, (Player === PLYR_TOKEN) ? COMP_TOKEN : PLYR_TOKEN);
//since it was a recursive thing, please do imagine we get here at max depth BEFORE lesser depths, and then we'll climb back when each depth return its value to the previous one
//so here the first "value" going in "values" will be the first cell where we did not go through "if (gameState == false){" : first cell where the game ended (with its associated cost, more on that later)
values.push({
cost: value,
cell: {
i: i,
j: j
}
});
}
}
//when the loop ended
//if we're simulating a computer turn
if (Player === COMP_TOKEN){
//getting the "value" with max cost out of "values"
const max = _.maxBy(values, (v) => {
return v.cost;
});
//if we endend our recursivity (we climbed all the way back to depth 0) == we are on the actual grid with no simulation
if( depth === 0 ){
return max.cell; //return the cell (computer will play this cell)
} else {
return max.cost; //else return the cost (to put in the "values" list)
}
}else{ //if we're simulating a player turn, same thing but with the min
const min = _.minBy(values, (v) => {
return v.cost;
});
if( depth === 0 ){ //may not be useful if you always call minimax at depth 0 on computer turn
return min.cell;
} else {
return min.cost;
}
}
} else if (gameState === null){ //so, here we're simulating our endgame, a draw have a value of 0
return 0;
} else if (gameState === PLYR_TOKEN){ //a player win have a value of "depth-10" (the quicker he win, the lesser the result)
return depth - 10;
} else if (gameState === COMP_TOKEN){ //a computer win have a value of "10-depth" (the quicker he win, the greater the result)
return 10 - depth;
}
}
With that done, we have a better understanding on how the code work, and why it doesn't work like it's supposed to.
Indeed, on computer turn, it only check for what's the quickest move to win. I'm not 100% sure of my solution, but you could for example try to fix it that way :
if (Player === COMP_TOKEN){
//[...]
//if we endend our recursivity (we climbed all the way back to depth 0) == we are on the actual grid with no simulation
if( depth === 0 ){
const min = _.minBy(values, (v) => {
return v.cost;
});
if(min.cost>=-9)return min.cell; //if player win in the next turn, prevent it instead
else return max.cell; //else return the best cell for computer
}
//[...]
It's far from perfect (you could get him with a 2-move win for example) and it's not tested, but I hope it's clearer for you now.
When you're done with your code and it's working like you want, don't hesitate to post it on codereview.stackexchange to get optimisation advices.
So, this might be a weird thing to try to do, but I'm curious if it's possible:
Say I have an associative array like this:
myarray[50] = 'test1'
myarray[100] = 'test2'
I can access 'test1' by it's key, of course:
myarray[50]; // returns 'test1'
But is there a way where if I have an index key of '60', that I can look in the array and if key 60 isn't there, get the value of the next "closest" key, '50'?
The use-case for this is that I am trying to set up cue-points for a video, and if the user seeks and misses a cue point, I want to display the information from the last cue point the user seeked beyond.
I think I can check for the existence of the key with the 'in' operator. But if it's not found, how can I get the "previous" or "next smallest" array key that DOES exist?
I assume the only way to do this is to iterate through the array, saving the "last" index value until the exit condition of "index > myKey" is found. The thing is, if it's a long video with lots of queue points and the user seeks frequently, iterating through the entire array of cue points each time might be slow. Is there a better, faster way to do this?
You'd have to write your own function:
function getClosestTo(val, array) {
if (array[val] !== undefined) {
return val;
} else {
var upper = val;
var upperMatched = false;
var lower = val;
var lowerMatched = false;
while(upper < this.length) {
if (array[++upper] !== undefined) {
upperMatched = true;
break;
};
};
while(lower > -1) {
if (array[--lower] !== undefined) {
lowerMatched = true;
break;
};
};
if (upperMatched && lowerMatched) {
return upper - val < val - lower ? upper : lower;
} else if (upperMatched) {
return upper;
} else if (lowerMatched) {
return lower;
};
};
return -1;
};
You could also add this as a method of the Array prototype, to make (what I think) is more readable:
Array.prototype.getClosestTo = function (val) {
if (this[val] !== undefined) {
return val;
} else {
var upper = val;
var upperMatched = false;
var lower = val;
var lowerMatched = false;
while(upper < this.length) {
if (this[++upper] !== undefined) {
upperMatched = true;
break;
};
};
while(lower > -1) {
if (this[--upper] !== undefined) {
lowerMatched = true;
break;
};
};
if (upperMatched && lowerMatched) {
return upper - val < val - lower ? upper : lower;
} else if (upperMatched) {
return upper;
} else if (lowerMatched) {
return lower;
};
};
return -1;
};
// Usage:
// var closestKey = theArray.getClosestTo(50);