I am currently working on a board game like chess.
I can't seem to make alternate turn work.
const clickPiece = (e: React.MouseEvent) => {
const element = e.target as HTMLElement;
const shogiBoard = shogiBoardRef.current;
let steps = 1;
if (steps % 2 === 1) {
setActivePlayer(Player.US);
steps++;
} else if (steps % 2 === 0) {
setActivePlayer(Player.ENEMY);
steps++;
}
if (element.classList.contains("shogiPiece") && shogiBoard && activePlayer) {
const takeX = Math.floor((e.clientX - shogiBoard.offsetLeft) / CELL_SIZE);
const takeY = Math.abs(Math.ceil((e.clientY - shogiBoard.offsetTop - BOARD_SIZE) / CELL_SIZE));
setTakePosition({
x: takeX,
y: takeY,
});
const x = e.clientX - CELL_SIZE / 2;
const y = e.clientY - CELL_SIZE / 2;
element.style.position = "absolute";
element.style.left = `${x}px`;
element.style.top = `${y}px`;
setActivePiece(element);
}
};
activePlayer initially Player.US which comes from an enum:
export enum Player {
ENEMY,
US,
}
activePlayer useState:
const [activePlayer, setActivePlayer] = useState<Player>(Player.US);
For me to alternate turns seemed the easiest when grabbing a piece, check which player is up then change, tried it with increasing the number of steps and check for remainder but it is not working.
Thank you in advance for the suggestions and help.
The root cause your code is not working is how you are using step.
Step is part of your application's state, so this should be an state, not a simple variable. Your are actually resetting step = 1 in each render
First define [step, setStep] using useState:
const [step, setStep] = useState<number>(1); // instead of let steps = 1
and then update the step like this:
setStep(i => i+1) // instead of steps++
You can use this function to toggle the active player: check which is the current player and then set the other one:
TS Playground
import {useCallback, useState} from 'react';
enum Player { ENEMY, US }
function Component () {
const [activePlayer, setActivePlayer] = useState<Player>(Player.US);
const setAlternatePlayer = useCallback(
() => setActivePlayer(player => player === Player.US ? Player.ENEMY : Player.US),
[],
);
// Use function "setAlternatePlayer"...
}
This is because you if condition is wrong !
Change your if statement to this:
if (steps % 2 === 0) {
setActivePlayer(Player.ENEMY);
steps++;
} else {
setActivePlayer(Player.US);
steps++;
}
This will work
I have a function called getRandomHexagram() which returns a random 6-character string that is used for props in my <hexagram string={stringstate} /> component in my I Ching divination app.
I also have a function called resettimer() which retrieves this random 6-character string multiple times and passes it to my component within a few seconds, so that while setInterval is running, the component will keep changing its appearance until clearInterval is called.
I would like to continuously update the state using resettimer() function run until it ends, and then use the new state in another function together with clearInterval.
What is strange to me is that while the if portion of the timer is running, the component continuously updates as it should. But when I try to call the final state in the } else { statement together with clearInterval, console always shows that stringstate is still "VVVVVV", or whatever was the last prop that my component used in my previous click, NOT what my component is currently using.
function getRandomHexagram() {
const flipcoins = () => {
return (
(Math.floor(Math.random() * 2) + 2) +
(Math.floor(Math.random() * 2) + 2) +
(Math.floor(Math.random() * 2) + 2)
);
};
const drawline = (coinvalue) => {
let line;
if (coinvalue == 6) {line = "B";}
if (coinvalue == 7) {line = "V";}
if (coinvalue == 8) {line = "P";}
if (coinvalue == 9) {line = "W";}
return line;
};
return (
drawline(flipcoins()) +
drawline(flipcoins()) +
drawline(flipcoins()) +
drawline(flipcoins()) +
drawline(flipcoins()) +
drawline(flipcoins())
);
}
function App() {
const [stringstate, setStringstate] = useState("VVVVVV");
function resettimer() {
var timesrun = 0;
var randomtime = Math.floor(Math.random() * 15) + 10;
const interval = setInterval(() => {
timesrun += 1;
if (timesrun < randomtime) {
thisString = getRandomHexagram();
setStringstate(thisString);
console.log("the current permutation is: " + thisString);
// console and component shows this correctly.
} else {
clearInterval(interval);
console.log("the final state state is: " + stringstate);
// Call another function here using the NEW stringstate
// But console shows that stringstate is still showing
// the old state of VVVVVV despite my component showing otherwise
}
}, 100);
}
... ...
return (
<div className="App">
<Button onClick={() => resettimer()}>
<Hexagram string={stringstate} />
</div>
);
}
When the button is clicked and the function invoked, my console shows this:
the current permutation is: VPPVPB
the current permutation is: VPPPVV
the current permutation is: BPBVBV
the current permutation is: VVPVPV
the current permutation is: PPBVPV
the current permutation is: PPPBBB
the current permutation is: VVPPPV
the final state state is: VVVVVV
Any idea why this is happening? Any advice is much appreciated.
I have a React app where I want to display lunar phases. To that end I tried to create an empty array and then map it appropriately:
import React from 'react'
export const MoonPhase = (props) => {
let start = new Date(props.start)
let end = new Date(props.end)
let delta = (end.getTime() - start.getTime()) / 1000
let quarters = new Array(Math.floor(
// Each lunar month is 2.55ᴇ6s.
(delta / (2.55 * Math.pow(10, 6))) * 4
))
return (
<div className='phases'>
{quarters.map((_, i) => {
switch(i % 4) {
case 0: return <span>🌑</span>
case 1: return <span>🌓</span>
case 2: return <span>🌕</span>
case 3: return <span>🌗</span>
}
})}
</div>
)
}
This doesn't produce anything, but if I change quarters to [1,2,3,4,5] I get output.
How should I approach this?
I am making a game of Snake using JS, html and CSS, having a problem with the JS. Specifically the collision event between the snake and fruit, where the snake eats the fruit and it disappears and reappears randomly. Is the if statement after the switch correct?
document.addEventListener('DOMContentLoaded', () => {
const squares = document.querySelectorAll('.grid div')
const width = 10
let currentIndex = 0
let snakes = [2,1,0] //2 is head, 1 is body, 0 is tail
let direction = 1
snakes.forEach(snake => squares[snake].classList.add('snake'))
setInterval(() => {
const tail = snakes.pop() //removes the last item of the array
squares[tail].classList.remove('snake')
snakes.unshift(snakes[0] + direction)
console.log(snakes)
squares[snakes[0]].classList.add('snake')
}, 1000)
function moveMySnake(e) {
squares[currentIndex].classList.remove('snake')
switch(e.keyCode) {
case 37:
direction = -1
break
case 38:
direction = -width
break
case 39:
direction = 1
break
case 40:
direction = width
break
}
if(snakes[0].contains('fruit')) {
snakes[0].remove('fruit')
fruitReappears()
}
function fruitReappears() {
const randomIndex = Math.floor(Math.random() * squares.length)
const randomSquare = squares[randomIndex]
randomSquare.classList.add('fruit')
}
document.addEventListener('keyup', moveMySnake)
document.addEventListener('keydown', e => e.preventDefault())
})
currently the snake just passes through the fruit and nothing happens?
I am getting a NaN return when I convert my letter input to number and then pass it to another function that totals and averages them. Is there a problem with my conversion function or the way I am calling the function?
function CalcGPA(a, b, c, d, e, f, g, h, i, j) {
var initial = a + b + c + d + e + f + g + h + i + j;
var total = initial / 10;
return total;
}
function Convert(z) {
var x = z.toString().toUpperCase();
switch (x.value) {
case "A":
return 4.0;
case "A-":
return 3.67;
case "B+":
return 3.33;
case "B":
return 3.0;
case "B-":
return 2.67;
case "C+":
return 2.33;
case "C":
return 2.0;
case "C-":
return 1.7;
case "D+":
return 1.3;
case "D":
return 1.0;
case "F":
return 0;
}
}
$(document).ready(function () {
var input2 = $('[name="grade1"],[name="grade2"],[name="grade3"],[name="grade4"],[name="grade5"],[name="grade6"],[name="grade7"],[name="grade8"],[name="grade9"],[name="grade10"],[name="grade11"]');
input2.keyup(function () {
var total2;
Convert((input2[0]));
Convert((input2[1]));
Convert((input2[2]));
Convert((input2[3]));
Convert((input2[4]));
Convert((input2[5]));
Convert((input2[6]));
Convert((input2[7]));
Convert((input2[8]));
Convert((input2[9]));
total2 = CalcGPA(input2[0], input2[1], input2[2], input2[3], input2[4], input2[5], input2[6], input2[7], input2[8], input2[9]);
total2.toFixed(2);
$(input2[10]).val(total2);
});
});
Call CalcGPA on the value obtained by running Convert() on the array elements. Alternatively, store the 'Converted' values to temporary variables, and then call CalcGPA with them as arguments. Something like this -
function CalcGPA(a, b, c, d, e, f, g, h, i, j) {
var initial = a + b + c + d + e + f + g + h + i + j;
var total = initial / 10;
return total;
}
function Convert(z) {
var x = z.toString().toUpperCase();
switch (x.value) {
case "A":
return 4.0;
case "A-":
return 3.67;
case "B+":
return 3.33;
case "B":
return 3.0;
case "B-":
return 2.67;
case "C+":
return 2.33;
case "C":
return 2.0;
case "C-":
return 1.7;
case "D+":
return 1.3;
case "D":
return 1.0;
case "F":
return 0;
}
}
$(document).ready(function () {
var input2 = $('[name="grade1"],[name="grade2"],[name="grade3"],[name="grade4"],[name="grade5"],[name="grade6"],[name="grade7"],[name="grade8"],[name="grade9"],[name="grade10"],[name="grade11"]');
input2.keyup(function () {
var total2;
total2 = CalcGPA(Convert(input2[0]), Convert(input2[1]), Convert(input2[2]), Convert(input2[3]), Convert(input2[4]), Convert(input2[5]), Convert(input2[6]), Convert(input2[7]), Convert(input2[8]), Convert(input2[9]));
total2.toFixed(2);
$(input2[10]).val(total2);
});
});
To answer your question:
You are not storing the value of your Convert() calls anywhere, so when you call CalcGPA(), you are just sending the original letter grades into the function.
To make your code better:
I would also strongly recommend that you rethink the way you are collecting values from the various input fields and using them within your calculations.
For example, I would start by applying a common class across the inputs. That way there could be 1 input or 20 inputs, and you could code to handle anything.
Let's say I give each input a class name of gradeLetterInput
Then you might be able to rewrite the $(document).ready() and CalcGPA() functions like this:
$(document).ready(function () {
// get collection of inputs
var $gradeLetterInputs = $('.gradeLetterInput');
var gpaValues = $.map($gradeLetterInputs, function($item, idx) {
return Convert($item.val());
});
var gpaAvg = CalcGPA(gpaValues);
// not shown place avg GPA on DOM somwhere
}
function CalcGPA(arr) {
var total = 0;
var count = arr.length;
for (i=0;i<count;i++) {
total += arr[i];
}
return total/count;
}
Anytime you see yourself relying on id's or input names like something# or adding a bunch of parameters to a function call for a set of similar items, you should recognize this as an anti-pattern and try to think about how you can refactor your code to eliminate such usage.