How can I make this JS repetitive code shorter - javascript

I just wrote this piece of code that does the thing it's supposed to do, although it's really messy and pretty repetitive and I'm wondering how can I make it much shorter and concise.
if(id==1 && player == "playerOne"){
Object.assign(playerOne, fighters[0])
}else if(id==1 && player =="playerTwo"){
Object.assign(playerTwo, fighters[0])
}
if(id==2 && player == "playerOne"){
Object.assign(playerOne, fighters[1])
}else if(id==2 && player =="playerTwo"){
Object.assign(playerTwo, fighters[1])
}
if(id==3 && player == "playerOne"){
Object.assign(playerOne, fighters[2])
}else if(id==3 && player =="playerTwo"){
Object.assign(playerTwo, fighters[2])
}
if(id==4 && player == "playerOne"){
Object.assign(playerOne, fighters[3])
}else if(id==4 && player =="playerTwo"){
Object.assign(playerTwo, fighters[3])
}
if(id==5 && player == "playerOne"){
Object.assign(playerOne, fighters[4])
}else if(id==5 && player =="playerTwo"){
Object.assign(playerTwo, fighters[4])
}
if(id==6 && player == "playerOne"){
Object.assign(playerOne, fighters[5])
}else if(id==6 && player =="playerTwo"){
Object.assign(playerTwo, fighters[5])
}
if(id==7 && player == "playerOne"){
Object.assign(playerOne, fighters[6])
}else if(id==7 && player =="playerTwo"){
Object.assign(playerTwo, fighters[6])
}
if(id==8 && player == "playerOne"){
Object.assign(playerOne, fighters[7])
}else if(id==8 && player =="playerTwo"){
Object.assign(playerTwo, fighters[7])
}
if(id==9 && player == "playerOne"){
Object.assign(playerOne, fighters[8])
}else if(id==9 && player =="playerTwo"){
Object.assign(playerTwo, fighters[8])
}
Thank you in advance!

Assuming
a) there are no more than two players
b) you don't care that this code handles id < 1 and id > 9
It looks to me like you could reduce this to a single line.
Object.assign(player == "playerOne" ? playerOne : playerTwo, fighters[id - 1])

What if you rewrite it to be something along the lines of
if (player == "playerOne") {
p = playerOne
} else if (player =="playerTwo") {
p = playerTwo
}
Object.assign(p, fighters[id - 1])

A for loop might be helpful:
for (let i = 1; i < 10; i++) {
if (id == i && player == "playerOne") {
Object.assign(playerOne, fighters[id - 1]);
} else if (id == i && player == "playerTwo") {
Object.assign(playerTwo, fighters[id - 1]);
}
}

Based on the assumptions in your example, that id must be between 1 and 9 and player can be anything, but must be playerOne or playerTwo:
for (var i = 1; i < 10; i++) {
if (id == i) {
if(player == "playerOne"){
Object.assign(playerOne, fighters[i - 1]);
} else if (player == "playerTwo") {
Object.assign(playerTwo, fighters[i - 1]);
}
}
}

Related

How to remove edge cases for string and number expressions

I'm building a rock, paper, scissors game and working on excluding edge cases.
The function is setPlayerMoves(), which is supposed to set previously undefined global variables for player moves and give them a 'value' between 1-99. how do I excluded edge cases like wrong move type (eg. 'rok') or wrong value (eg. 100).
Here is my incorrect code:
function setPlayerMoves(player, moveOneType, moveOneValue, moveTwoType, moveTwoValue, moveThreeType, moveThreeValue) {
if (player === 'Player One' && (moveOneType && moveTwoType && moveThreeType === 'rock' || 'paper' || 'scissors') && (moveOneValue && moveTwoValue && moveThreeValue === (>= 1 && <= 99))) {
playerOneMoveOneType = 'rock';
playerOneMoveTwoType = 'paper';
playerOneMoveThreeType = 'scissors';
playerOneMoveOneValue = 11;
playerOneMoveTwoValue = 33;
playerOneMoveThreeValue = 55;
} else if (player === 'Player Two') {
playerTwoMoveOneType = 'rock';
playerTwoMoveTwoType = 'paper';
playerTwoMoveThreeType = 'scissors';
playerTwoMoveOneValue = 11;
playerTwoMoveTwoValue = 33;
playerTwoMoveThreeValue = 55;
}
}
Problem
Code for checking edge cases are wrong.
(player === 'Player One' && (moveOneType && moveTwoType && moveThreeType === 'rock' || 'paper' || 'scissors') && (moveOneValue && moveTwoValue && moveThreeValue === (>= 1 && <= 99)))
Also you are checking the edge cases for only 'player one'
Solution
Check the values seperately before assigning values and create a valid variable
let valid = false
let states = ['rock','paper','scissors'];
if((states.indexOf(moveOneType) != -1) && (states.indexOf(moveTwoType) != -1) && (states.indexOf(moveThreeType) != -1)){
// valid state
valid = true;
}else{
valid = false;
}
if((moveOneValue >=1 && moveOneValue <= 99) && (moveTwoValue >=1 && moveTwoValue <= 99) && (moveThreeValue >=1 && moveThreeValue <= 99)){
//valid value
valid = true;
}else{
valid = false
}
Then assign the values.
if ((player === 'Player One') && valid) {
playerOneMoveOneType = moveOneType;
playerOneMoveTwoType = moveTwoType;
playerOneMoveThreeType = moveThreeType;
playerOneMoveOneValue = moveOneValue;
playerOneMoveTwoValue = moveTwoValue;
playerOneMoveThreeValue = moveThreeValue;
} else if ((player === 'Player Two') && valid) {
playerTwoMoveOneType = moveOneType;
playerTwoMoveTwoType = moveTwoType;
playerTwoMoveThreeType = moveThreeType;
playerTwoMoveOneValue = moveOneValue;
playerTwoMoveTwoValue = moveTwoValue;
playerTwoMoveThreeValue = moveThreeValue;
}
Also if possible
Create an object each for player1 and player2 like below and use.
let player1 = {
moveOneType:'rock'
moveTwoType:'paper'
moveThreeType:'scissors'
moveOneValue: 23
moveTwoValue: 33
moveThreeValue: 98
}

Sqrt and Cbrt aren't accurate when squared or cubed

Building a calculator and I'm implementing cube roots and square roots, but when reversed by squaring them or cubing them respectively, it is off by a very small, but it is noticeable and ugly in the calculator when there are several zeros and a 1 after it. Any way to prevent this, but still make the calculator accurate.
function click22() {
if (vi === 0) {
reactant = Math.sqrt(reactant);
} else if (vi !== 0 && tn === 0) {
reactant2 = Math.sqrt(reactant2);
} else if (vi !== 0 && tn !== 0) {
reactantspec = Math.sqrt(reactantspec);
}
}
function click23() {
if (vi === 0) {
reactant = Math.cbrt(reactant);
} else if (vi !== 0 && tn === 0) {
reactant2 = Math.cbrt(reactant2);
} else if (vi !== 0 && tn !== 0) {
reactantspec = Math.cbrt(reactantspec);
}
}
if (vi === 0 && reactant != "0"){
document.getElementById('result').innerHTML = reactant;
} else if (vi > 1 && reactant2 != "" && tx === 0) {
document.getElementById('result').innerHTML = reactant2;
} else if (vi > 1 && tx > 0) {
document.getElementById('result').innerHTML = reactantspec;
}
}

why I'm getting "max call stack size exceeded" error

I'm working on minimax part of a tic tac toe game, and I keep getting "max call stack size exceeded" error. I isolated the bug to line # 110 which is emptyIndexies(board) function. But I don't see anything wrong with the function. I tested it on the console, and the board seems to update when I click on it; however, the computer turn don't work. Here's the code
var board = [0, 1, 2, 3, 4, 5, 6, 7, 8];
var player, sqrId, user, computer, row, col;
const ARR_LENGTH = 9;
$(document).ready(function() {
//1 checkbox event listener
$(".checkBox").click(function() {
if($(this).is(":checked")) {
user = $(this).val();
player = user;
computer = (user == 'X') ? 'O' : 'X';
}
});
//2 square even listener
$(".square").click(function() {
sqrId = $(this).attr("id");
playerMove();
minimax(board);
if(checkWinner(board, turn)) {
alert(turn+" Wins the game!");
resetBoard();
}
if(!checkDraw()) {
alert("It's a draw!");
}
player = (player == user) ? computer : user;
});
//reset board
$(".reset").click(function() {
resetBoard();
})
});
//player move
function playerMove() {
if($("#"+sqrId).text() == "") {
$("#"+sqrId).text(player);
board[sqrId] = player;
console.log(board);
}
else {
alert("Wrong move");
}
}
/* computer AI generate random number between 0 - 8 */
function computerAI() {
var random;
var min = 0, max = 8;
do {
random = Math.floor(Math.random() * (max + min));
}while($("#"+random).text() != "")
$("#"+random).text(computer);
row = getRow();
col = getCol();
board[row][col] = computer;
}
//getting row number
function getRow() {
return Math.floor(sqrId / ARR_LENGTH);
}
//getting col number
function getCol() {
return sqrId % ARR_LENGTH;
}
/* checking for winner */
// winning combinations using the board indexies
function winning(board){
if (
(board[0] == player && board[1] == player && board[2] == player) ||
(board[3] == player && board[4] == player && board[5] == player) ||
(board[6] == player && board[7] == player && board[8] == player) ||
(board[0] == player && board[3] == player && board[6] == player) ||
(board[1] == player && board[4] == player && board[7] == player) ||
(board[2] == player && board[5] == player && board[8] == player) ||
(board[0] == player && board[4] == player && board[8] == player) ||
(board[2] == player && board[4] == player && board[6] == player)
) {
return true;
} else {
return false;
}
}
function resetBoard() {
$(".square").text("");
$(".checkBox").prop("checked", false);
user = "";
turn = "";
computer = "";
for(var i = 0; i < ARR_LENGTH; i++) {
board[i] = "";
}
}
// returns list of the indexes of empty spots on the board
function emptyIndexies(board){
return board.filter(s => s != "O" && s != "X");
}
// the main minimax function
function minimax(newBoard, player){
//available spots
var availSpots = emptyIndexies(newBoard);
// checks for the terminal states such as win, lose, and tie
//and returning a value accordingly
if (winning(newBoard, user)){
return {score:-10};
}
else if (winning(newBoard, computer)){
return {score:10};
}
else if (availSpots.length === 0){
return {score:0};
}
// an array to collect all the objects
var moves = [];
// loop through available spots
for (var i = 0; i < availSpots.length; i++){
//create an object for each and store the index of that spot
var move = {};
move.index = newBoard[availSpots[i]];
// set the empty spot to the current player
newBoard[availSpots[i]] = player;
/*collect the score resulted from calling minimax
on the opponent of the current player*/
if (player == computer){
var result = minimax(newBoard, user);
move.score = result.score;
}
else{
var result = minimax(newBoard, computer);
move.score = result.score;
}
// reset the spot to empty
newBoard[availSpots[i]] = move.index;
// push the object to the array
moves.push(move);
}
// if it is the computer's turn loop over the moves and choose the move with the highest score
var bestMove;
if(player === computer){
var bestScore = -10000;
for(var i = 0; i < moves.length; i++){
if(moves[i].score > bestScore){
bestScore = moves[i].score;
bestMove = i;
}
}
}else{
// else loop over the moves and choose the move with the lowest score
var bestScore = 10000;
for(var i = 0; i < moves.length; i++){
if(moves[i].score < bestScore){
bestScore = moves[i].score;
bestMove = i;
}
}
}
// return the chosen move (object) from the moves array
return moves[bestMove];
}

rps function not working (javascript)

Rps game code isn't working. My battle function is supposed to set win to a 1,2, or 3 but it always stays at the default 0. I've checked the other values through the console, and they all seem to be working. here is my code.
function Player(number) {
this.number = number;
this.choice = 0;
}
var player1 = new Player(1);
var player2 = new Player(2);
var win = 0;
var battle = function() {
if (player1.choice === player2.choice) {
win = 3;
} else if (player1.choice + 2 === player2.choice && player1.choice === 1){
win = 2;
} else if (player1.choice + 1 === player2.choice && player1.choice === 1) {
win = 1;
} else if (player1.choice + 1 === player2.choice && player1.choice === 2) {
win = 1;
} else if (player1.choice - 1 === player2.choice && player1.choice === 2) {
win = 2;
} else if (player1.choice - 1 === player2.choice && player1.choice === 3) {
win = 2;
} else if (player1.choice - 2 === player2.choice && player1.choice === 3) {
win = 1;
} else {
alert ('someone pressed the wrong button')
}
}
var Reset = function () {
win = 0;
player1.choice = 0;
player2.choice = 0;
}
$(document).ready(function() {
$(document).keydown(function(event) {
if (event.which === 81) {
player1.choice = 1;
} else if (event.which === 87){
player1.choice = 2;
} else if (event.which === 69){
player1.choice = 3;
} else if (event.which === 37){
player2.choice = 1;
} else if (event.which === 40){
player2.choice = 2;
} else if (event.which === 39){
player2.choice = 3;
}
})
if (player1.choice > 0 && player2.choice > 0) {
battle();
if (win === 1) {
$('.winner').append('<p>player1 wins!</p>')
} else if (win === 2) {
$('.winner').append('<p>player2 wins!</p>')
} else if (win === 3) {
$('.winner').append('<p>It is a draw!</p>')
}
}
})
My html has the div with class 'winner' in it.
Additional question
How do I cancel the keydown function after both players have chosen their options.
Are you giving your player a choice in the Player class you created? It should look like this:
function Player(number, choice) {
this.number = number;
this.choice = choice;
}

Refactoring long if/else if statement with for loop - javascript

I know there is a way for me to refactor this code, but I can't figure it out and I could use some help. I created a tic tac toe game and created the functionality to declare a winner, it works, but it's way too long.
$(document).ready(function() {
var turn = 0;
// var winOptions = [['#1','#2','#3'], ['#4','#5','#6'], ['#7','#8','#9'], [1,5,9], [3,5,7], [1,4,7], [3,6,9], [2,4,8]];
$('td').on("click", function() {
if (turn % 2 === 0) {
$(this).text("0");
} else {
$(this).text("X");
}
$(this).off("click");
turn++;
checkForWinner()
});
function checkForWinner() {
if ($('#1').text()==='X' && $('#2').text()==='X' && $('#3').text()==='X')
alert('you win!');
else if ($('#4').text()==='X' && $('#5').text()==='X' && $('#6').text()==='X')
alert('you win!');
else if ($('#7').text()==='X' && $('#8').text()==='X' && $('#9').text()==='X')
alert('you win!');
else if ($('#1').text()==='X' && $('#5').text()==='X' && $('#9').text()==='X')
alert('you win!');
else if ($('#3').text()==='X' && $('#5').text()==='X' && $('#7').text()==='X')
alert('you win!');
else if ($('#1').text()==='X' && $('#4').text()==='X' && $('#7').text()==='X')
alert('you win!');
else if ($('#3').text()==='X' && $('#6').text()==='X' && $('#9').text()==='X')
alert('you win!');
else if ($('#2').text()==='X' && $('#4').text()==='X' && $('#8').text()==='X')
alert('you win!');
else if ($('#1').text()==='O' && $('#2').text()==='O' && $('#3').text()==='O')
alert('you win!');
else if ($('#4').text()==='O' && $('#5').text()==='O' && $('#6').text()==='O')
alert('you win!');
else if ($('#7').text()==='O' && $('#8').text()==='O' && $('#9').text()==='O')
alert('you win!');
else if ($('#1').text()==='O' && $('#5').text()==='O' && $('#9').text()==='O')
alert('you win!');
else if ($('#3').text()==='O' && $('#5').text()==='O' && $('#7').text()==='O')
alert('you win!');
else if ($('#1').text()==='O' && $('#4').text()==='O' && $('#7').text()==='O')
alert('you win!');
else if ($('#3').text()==='O' && $('#6').text()==='O' && $('#9').text()==='O')
alert('you win!');
else if ($('#2').text()==='O' && $('#4').text()==='O' && $('#8').text()==='O')
alert('you win!');
}
});
I started making the array variable win options to loop through, but it still isn't working.
FYI, those selectors are the id's for each td tag in my HTML.
Thanks for taking a look at this.
Some refactoring along the lines of Shilly's comment might be called for. However, without drastically changing your design, you could do something like the following:
var winConditions = [
['#1','#2','#3'], ['#4','#5','#6'], ['#7','#8','#9'], // rows
['#1','#4','#7'], ['#2','#5','#8'], ['#3','#6','#9'], // columns
['#1','#5','#9'], ['#3','#5','#7'] // diagonals
];
for (var i = 0, len = winConditions.length; i < len; i++) {
var text = $(winConditions[i][0]).text();
if (
(text === 'X' || text === 'O') &&
$(winConditions[i][1]).text() === text && $(winConditions[i][2]).text() === text
) {
alert('you win!');
break;
}
}

Categories