Problem: I´m pushing a user input into an empty array, for my tic tac toe game.
We have 3 arrays so that it looks like the board inside the terminal.
How can I check if the board is full or if the place inside the array is taken?
I don´t know if my question makes sense, if not I will try to explain it further.
Now the code:
const prompt = require('prompt-sync')();
//Functions
function displayText(text) {
console.log(text);
}
function getUserInput() {
let userMove = prompt(displayText(MOVE_INSTRUCTION)).toUpperCase();
MOVES_MADE.push(userMove);
}
function checkUserInput(input) {
/*if(input === MOVES_MADE) {
console.log("Please enter coordinates of a free field")
}//add placeholder*/
if (input === "A1") {
GRID[0].splice(0, 1, "X"); //an Stelle A1 wird mit splice ein X eingesetzt
displayText(GRID)
} //neues Grid wird angezeigt
else if (input === "A2") {
GRID[0].splice(1, 1, "X");
displayText(GRID)
} else if (input === "A3") {
GRID[0].splice(2, 1, "X");
displayText(GRID)
} else if (input === "B1") {
GRID[1].splice(0, 1, "X");
displayText(GRID)
} else if (input === "B2") {
GRID[1].splice(1, 1, "X");
displayText(GRID)
} else if (input === "B3") {
GRID[1].splice(1, 1, "X");
displayText(GRID)
} else if (input === "C1") {
GRID[2].splice(0, 1, "X");
displayText(GRID)
} else if (input === "C2") {
GRID[2].splice(1, 1, "X");
displayText(GRID)
} else if (input === "C3") {
GRID[1].splice(2, 1, "X");
displayText(GRID)
} else {
displayText(WRONG_ENTRY)
};
}
//Variables
//Texts
const INTRO_TEXT = "What Mode would you like to play?";
const GAME_MODES = ["1. Human vs Human", "2. Random AI vs Random AI", "3. Human vs Random AI", "4. Human vs Unbeatable AI"];
const CHOOSE_MODES = "Choose by entering number in front of option.";
const GAME_MODE_TEXT = "You chose: ";
const MOVE_INSTRUCTION = "Please enter a move."
const WRONG_ENTRY = "Falsche Eingabe"
let GRID = [
[".", ".", "."],
[".", ".", "."],
[".", ".", "."]
]
let NUM_PICKED = [];
let MOVES_MADE = [];
//main
displayText(INTRO_TEXT);
displayText(GAME_MODES);
let playMode = prompt(displayText(CHOOSE_MODES));
NUM_PICKED.push(playMode);
if (Number(NUM_PICKED[0]) === 1) {
displayText(`${GAME_MODE_TEXT}: ${GAME_MODES[0]}`); //displaying text: You chose 1. Human vs Human
displayText(GRID);
getUserInput(); //asks player for a move
checkUserInput(MOVES_MADE[0]);
} else if (Number(NUM_PICKED[0]) === 2) {
displayText(`${GAME_MODE_TEXT}: ${GAME_MODES[1]}`); //displaying text: You chose 2. Random AI vs Random AI
displayText(GRID);
getUserInput(); //asks player for a move
checkUserInput(MOVES_MADE[0]);
} else if (Number(NUM_PICKED[0]) === 3) {
displayText(`${GAME_MODE_TEXT}: ${GAME_MODES[2]}`); //displaying text: You chose 3. Human vs Random AI
displayText(GRID);
getUserInput(); //asks player for a move
checkUserInput(MOVES_MADE[0]);
} else if (Number(NUM_PICKED[0]) === 4) {
displayText(`${GAME_MODE_TEXT}: ${GAME_MODES[3]}`); //displaying text: You chose 4. Human vs Unbeatable AI
displayText(GRID);
getUserInput(); //asks player for a move
checkUserInput(MOVES_MADE[0]);
} else {
displayText("WRONG ENTRY: This mode doesn't exist")
}
if (playMode === 1) {
displayText(`${GAME_MODE_TEXT}: + ${GAME_MODES[0]}`); //displaying text: You chose 1. Human vs Human
} else if (playMode === 2) {
displayText(`${GAME_MODE_TEXT}: + ${GAME_MODES[1]}`); //displaying text: You chose 2. Random AI vs Random AI
} else if (playMode === 3) {
displayText(`${GAME_MODE_TEXT}: + ${GAME_MODES[2]}`); //displaying text: You chose 3. Human vs Random AI
} else if (playMode === 4) {
displayText(`${GAME_MODE_TEXT}: + ${GAME_MODES[3]}`); //displaying text: You chose 4. Human vs Unbeatable AI
}
/*
const (DATA) = require("./date.js");
console.log("DATA");
*/
// checking if array is taken
We have our grid made out of 3 arrays, with three times . as a value that is replaced by the players input. We defined each value in the arrays to a coordinate. Now I want to check if the coordinate is already taken but before that I want to check if the board is full.
You can check any given index in your 2D array with a function like so.
// returns true if the "cell" is "empty", otherwise false
const isCellEmpty = (row, col) => GRID[row][col] === emptyCell;
And you can easily check if a 2D array contains "empty" cells using some and includes
// returns true if there are any empty "cells", otherwise false
const gridHasSpace = () => GRID.some(x => x.includes(emptyCell));
For example...
// values that represent cell states
const empty = '.';
const naught = '0';
const cross = 'X';
// returns true if the "cell" is "empty", otherwise false
const isCellEmpty = (row, col) => GRID[row][col] === empty;
// returns true if there are any empty "cells", otherwise false
const gridHasSpace = () => GRID.some(x => x.includes(empty));
const GRID = [
[empty, naught, cross],
[naught, empty, cross]
[naught, empty, cross]
]
console.log(isCellEmpty(0, 0)); // true
console.log(isCellEmpty(0, 2)); // false
console.log(gridHasSpace()); // true
what do you mean by full. You can check length of an array using array.length, in your case this would be MOVES_MADE.length.
Alternatively, you might declare a variable that increases whenever you push an element into an array and check this variable in the future. I am not sure what you are trying to achieve.
or there is another way which might solve your problem see this code if (typeof array[x]==="undefined"){ console.log("this is undefined"); }
Related
I am writing a function that takes a 2d array that represents a grid for a tic tac toe game. Initially it is [[0, 0, 0], [0, 0, 0], [0, 0, 0]] and there is player 1 which is represented as 1 and player 2 as -1.
The function to check the winner should return the winner if there is actually a winner.
My attempt is this:
const checkIfWin = (b) => {
if (b[0][0] == b[0][1] && b[0][1] == b[0][2]) return b[0][0]
if (b[1][0] == b[1][1] && b[1][2] == b[1][1]) return b[1][0]
if (b[2][0] == b[2][1] && b[2][1] == b[2][2]) return b[2][2]
if (b[0][0] == b[1][0] && b[1][0] == b[2][0]) return b[2][0]
if (b[0][1] == b[1][1] && b[1][1] == b[2][1]) return b[2][1]
if (b[0][2] == b[1][2] && b[1][2] == b[2][2]) return b[2][2]
if (b[0][0] == b[1][1] && b[1][1] == b[2][2]) return b[2][2]
if (b[0][2] == b[1][1] && b[1][1] == b[2][0]) return b[2][0]
}
checkIfWin([ [ 1, 0, 0 ], [ 0, 1, 0 ], [ -1, -1, 1 ] ]) // 1
It works but I don't think it is really elegant or clean and I wonder if there is a better way to check a winner and return the winner?
Use two helper functions: checkRow(i) and checkColumn(i), which check if all values are same in a given row and column respectively.
For i in range [0, number of rows], use checkRow(i)
For i in range [0, number of columns], use checkColumn(i)
If still no winner is found, check the diagonals (for which you can use another helper function)
This is extremely simple to program, so I'll leave that up to you. But you can use this approach to break down the problem into simpler pieces.
You could write a generic function to check how many cells in a row have the same value, given a starting cell and an offset.
For rows the offset would be 1.
For columns the offset would be the board size.
For diagonals the offset would be the board size plus or minus one.
const boardSize = 3;
// board state in a flat array to
// simplify jumping to the next cell
// via a given offset
const state = [
1, 0, 0,
1, 1, 0,
1, 1, 0,
]
function numInARow(cellIndex, offset) {
let value = state[cellIndex];
let result = 1;
while(state[cellIndex += offset] === value) {
result++;
}
return result;
}
console.log(numInARow(0, 1)); // first row (1)
console.log(numInARow(0, 3)); // first column (3)
console.log(numInARow(0, 4)); // diagonal from top left (2)
With that established you could use a loop to check each row and column.
for(let i = 0; i < boardSize; i++) {
if(numInARow(state, i, 1) >= boardSize) {
// row win
}
if(numInARow(state, boardSize * i >= boardSize) {
// column win
}
if(numInARow(state, i, boardSize + 1) >= boardSize {
// diagonal pointing downhill to the right
}
if(numInARow(state, i, boardSize - 1) >= boardSize {
// diagonal pointing uphill to the right
}
}
The diagonals don't really need the loop if you're only checking from corner to corner, but it doesn't hurt anything to have them in there for a board this small.
Another thing to consider is that you only need to check around the last square played. My demo code only checks forward from the starting cell (to the right and down) but it could easily be tweaked to also check backwards. This would improve performance for massive board sizes because you wouldn't be checkin the entire board every time.
There are many ways to do this. You could rely on regular expressions and represent your board as a single string with 9 characters.
Here is an implementation of that idea:
class TicTacToe {
constructor() {
this.board = ".........";
this.mover = "O"; // Player that made last move
this.hasWon = false;
this.isDraw = false;
}
play(i) {
if (this.hasWon || this.board[i] !== ".") return; // Invalid move
this.mover = "XO"["OX".indexOf(this.mover)]; // Toggle player
this.board = this.board.slice(0, i) + this.mover + this.board.slice(i + 1);
this.hasWon = /(\w)(\1\1(...)*$|...\1...\1|..\1..\1|.\1.\1..$)/.test(this.board);
this.isDraw = !this.hasWon && !this.board.includes(".");
}
}
// I/O handling
document.querySelector("table").addEventListener("click", function (e) {
const cell = e.target;
if (cell.tagName !== "TD") return;
const move = cell.parentNode.rowIndex * 3 + cell.cellIndex
game.play(move);
cell.textContent = game.mover;
const message = game.hasWon ? game.mover + " has won!" :
game.isDraw ? "It's a draw" : "Click to play";
document.querySelector("span").textContent = message;
});
document.querySelector("button").addEventListener("click", newGame);
function newGame() {
game = new TicTacToe;
for (const td of document.querySelectorAll("td")) td.textContent = "";
document.querySelector("span").textContent = "Click to play";
};
newGame();
table { border-collapse: collapse }
td { vertical-align: middle; text-align: center; min-width: 24px; height: 24px; border: 1px solid; }
<span></span>
<table>
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
<tr><td></td><td></td><td></td></tr>
</table>
<button>New game</button>
I'm building a Discord bot using a collector and am trying to collect votes for rounds of a game. However, the bot keeps returning the wrong values. It will usually return either undefined or [object Object]. The stringifier() function should be returning the name of a round but it's instead returning undefined. e.g. if I input "spleef" on Discord it should output "Spleef" to console and in Discord, but it returns undefined in both or just errors.
UPDATE: i found that the variables were returning boolean values (the variables are battleBoxVotes to cTFVotes).
UPDATE A LOT LATER: so I fixed the problem but my new problem now is that I can't get variables bbv to ctfv to not return an error if they don't have a value. Console logs this: (node:8700) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'content' of undefined
const filter = m => (m.author.id != client.user.id);
const channel = message.channel;
const collector = channel.createMessageCollector(filter, { time: 5000 });
collector.on('collect', m => console.log(`Collected ${m.content}`));
collector.on('end', async collected => {
var bb = collected.find(collected => collected.content === 'battle box');
var pw = collected.find(collected => collected.content === 'spleef');
var s = collected.find(collected => collected.content === 'spleef');
var sw = collected.find(collected => collected.content === 'skywars');
var ctf = collected.find(collected => collected.content === 'capture the flag');
const bbv = bb.content || null;
const pwv = pw.content || null;
const sv = s.content || null;
const swv = sw.content || null;
const ctfv = ctf.content || null;
const stringifier = function(a, b, c, d, e) {
let results;
if (a>b&&a>c&&a>d&&a>e) {results = "Battle Box"}
else if (b>a&&b>c&&b>d&&b>e) {results = "Parkour Warrior"}
else if (c>a&&c>b&&c>d&&c>e) {results = "Spleef"}
else if (d>a&&d>b&&d>c&&d>e) {results = "SkyWars"}
else if (e>a&&e>b&&e>c&&e>c) {results = "Capture the Flag"}
return results;
}
message.channel.send(`And the winner is... ${stringifier(bbv, pwv, sv, swv, ctfv)}!`),
console.log(stringifier(bbv, pwv, sv, swv, ctfv))
});
The logic of that comparison absolutely breaks my brain and is a pain to read in the first place. Here's an adapted version that should work a bit better.
This solution iterates through the recieved messages and increments a counter for each gamemode depending on a switch-case expression. (bear in mind this doesn't stop a user from simply spamming whichever gamemode they want...)
Credit for the getAllIndexes function (used to determine ties)
const filter = m => (m.author.id != client.user.id);
const channel = message.channel;
const collector = channel.createMessageCollector(filter, { time: 5000 });
collector.on('collect', m => console.log(`Collected ${m.content}`));
collector.on('end', async collected => {
let msgs = Array.from(collected.keys()).map(m=>m?.content.toLowerCase().trim());
let modes = [ "Battle Box", "Parkour Warrior", "Spleef", "SkyWars", "Capture the Flag" ] // Define the names of the GameModes
let scores = [ 0, 0, 0, 0, 0 ] // Set up the default scores
msgs.forEach(m => {
switch(m) {
case "bb": // Fallthrough case for aliases
case "battlebox":
case "battle box":
scores[0]++; // Increment BattleBox (0) Counter
break;
case "pw":
case "parkour":
case "parkour warrior":
scores[1]++; // Increment ParkourWarrior (1) counter
break;
case "spleef":
scores[2]++; // Increment Spleef (2) counter
break;
case "sw":
case "sky":
case "skywars":
scores[3]++; // Increment SkyWars (3) counter
break;
case "ctf":
case "capture the flag":
scores[4]++; // Increment CaptureTheFlag (4) counter
break;
});
// Now to find who won...
let winningCount = Math.max(scores);
let winningIndexes = getAllIndexes(scores, winningCount);
if(winningIndexes.length = 1) { // Single Winner
message.channel.send(`And the winner is... ${modes[winningIndexes[0]]}!`);
} else { // tie!
message.channel.send(`It's a tie! There were ${winningIndexes.length} winners. (${winningIndexes.map(i=>modes[i]).join(", ")})`);
}
j});
// Get All Indexes function...
function getAllIndexes(arr, val) {
var indexes = [], i;
for(i = 0; i < arr.length; i++)
if (arr[i] === val) indexes.push(i);
return indexes;
}
I'm completing a JavaScript Codecademy course and am having trouble with one of the independent projects that requires you to design a number guessing game. For some reason, when "0" is involved, it returns incorrectly.
JavaScript and I aren't best friends as is, but I had a hard time with what's a relatively simple project and decided to try out one of the "challenge yourself" sections and I just can't make it work at all, or work out what's going wrong.
function generateTarget() {
return Math.floor(Math.random() * 10);
};
function compareGuesses(userGuess, computerGuess, targetNumber) {
function getAbsoluteDistance() {
const uGuess = Math.abs(generateTarget() - userGuess);
const cGuess = Math.abs(generateTarget() - computerGuess);
return uGuess - cGuess
};
if (getAbsoluteDistance() <= 0) {
return true
} else {
return false
};
};
// 'human' is input when return is true; 'computer' when return is false
function updateScore(winner) {
if (winner === 'human') {
humanScore ++;
} else if (winner === 'computer') {
computerScore ++;
};
};
function advanceRound() {
currentRoundNumber +=1;
};
It's essentially a game where the computer generates a random "target" number, you enter a guess number and the computer generates a guess number. Depending on which guess number is closer, you or the computer wins. If you guess the same number, or your guesses are equidistant, the human is meant to win.
This is (just about) working now, except in cases when:
the "target" number is 0. (The computer always wins)
you and the computer guess the same number. (The computer always wins)
you and the computer guess different but equidistant numbers from the target number (The computer always wins)
I'd like to try and get my head around some basic JavaScript so I can move on to something else for a bit, but can't work out what's wrong at all or how to fix it. The only thing that I can identify that these cases have in common is the fact that there's always a "0" somewhere in the absolute distance calculation.
Any help would be massively appreciated!
Kind of made it interactive. From what you say the user wins in the event of a tie or when you guess the same number (a tie). I think the comments indicated why it was failing initially, or at least what is what I see. You were comparing to two different targets, and you can see that in a Fiddle because the results were not consistent.
const userinput = document.getElementById("user");
const computerinput = document.getElementById("computer");
const targetvalue = document.getElementById("target");
const roundcount = document.getElementById("round");
var humanScore = 0;
var computerScore = 0;
var rounds = 0;
document.getElementById("generate").addEventListener("click", function(e) {
targetvalue.value = generateTarget();
});
document.getElementById("calc").addEventListener("click", function(e) {
let result = compareGuesses(userinput.value, computerinput.value, targetvalue.value);
if(result) humanScore++
else computerScore++
rounds++
roundcount.innerHTML = rounds;
alert("Score: Human: " + humanScore + ", Computer: " + computerScore );
});
function generateTarget() {
return Math.floor(Math.random() * 10);
}
function compareGuesses(userGuess, computerGuess, targetNumber) {
const uGuess = Math.abs(targetNumber - userGuess);
const cGuess = Math.abs(targetNumber - computerGuess);
if (uGuess - cGuess <= 0) {
return true
} else {
return false
}
}
<input id = "user" value="" placeholder ="user">
<input id = "computer" value="" placeholder ="computer"><br>
<input id = "target" value="" placeholder ="target">
<button id ="calc" type = "submit">
Submit
</button>
<button id ="generate" type = "submit">
Generate Number
</button><br>
Round: <span id = "round"></span>
The Snippet should allow testing easily. Did not add a thing to generate the computer guess, but that would be similar to generating a target.
I would do like:
function NumGuessGame(){
this.computerScore = this.score = 0;
this.guess = function(num){
if(num === Math.floor(Math.random()*10)){
this.score++;
}
else{
this.computerScore++;
}
return this;
}
}
var gg = new NumGuessGame;
gg.guess(9).guess(11).guess(7).guess(7);
if(gg.computerScore >= gg.score){
console.log('Computer Wins');
}
else{
console.log('You Win');
}
Of course, you should notice that the computer will almost always win under this scenario. You should notice the odds go up here:
function NumGuessGame(){
this.computerScore = this.score = 0;
this.guess = function(num){
if(num === Math.floor(Math.random()*10)){
this.score++;
}
else{
this.computerScore++;
}
return this;
}
}
var gg = new NumGuessGame;
gg.guess(4);
if(gg.computerScore >= gg.score){
console.log('Computer Wins');
}
else{
console.log('You Win');
}
Note that the examples above expect a 0-9 guess or you lose no matter what.
I'm trying to learn javascript and how to use the developer api provided by discord.
I do believe everything I want so far is working except I wanted to create a system like how a database works. Where each player in the table has a unique id key. I'm not sure if this is possible without using a db.
[Index.js]
/* Discord API Information */
const Discord = require('discord.js');
const token = '';
const Game = require('./game.js');
const client = new Discord.Client();
let playersName = []; // tracks each player that joins
let currPlayers = 0; //tracker for total players online
let INIT_GAME;
let userJoined = false;
let inBattle = false;
client.on('message', (msg) => {
if(msg.content === '!join'){
// Prevents multiple instances of the same person from joining
for(var x = 0; x < playersName.length; x++){
if(playersName[x]===msg.author.username){
return playersName[x];
}
}
currPlayers++;
userJoined = true;
playersName.push(msg.author.username);
//My attempt at having the question im asking
function convertToID(arr, width) {
return arr.reduce(function (rows, key, index) {
return (index % width == 0 ? rows.push([key])
: rows[rows.length-1].push(key)) && rows;
}, []);
}
console.log(convertToID(playersName,1)); /* Tracks players by ID in developer tools */
INIT_GAME = new Game(playersName, client, 'bot-testing', currPlayers);
let myRet = INIT_GAME.startGame();
const embed = new Discord.RichEmbed()
.setTitle("Welcome To Era Online")
.setColor(0xFF0000)
.addField(`${msg.author.username} has Joined`, myRet);
msg.channel.send(embed);
msg.channel.send(`${msg.author} has joined the game.`);
return;
}
if(userJoined == true){
if(msg.content === '!fight' && (!inBattle)){
let grabCurrPlayer = msg.author.username;
msg.channel.send(`${INIT_GAME.initBattle(grabCurrPlayer)}`);
}
else if(msg.content === '!leave'){
let tempLeave = msg.author.username;
for(var y = 0; y < playersName.length; y++){
if(playersName[y] == msg.author.username){
playersName[y] = [`${playersName[y]} was previously ID: ` + [y]];
currPlayers--;
}
}
msg.channel.send([`${tempLeave} has left the server.`]);
userJoined = false;
}
else if(msg.content === '!newgame'){
msg.channel.send(INIT_GAME.newGame());
}
/* Simply checks the bonus damage. command for developer*/
else if(msg.content === '!bonus'){
msg.channel.send(INIT_GAME.bonusAttack());
}
}
/* checks whose currently online. command for developer*/
if(msg.content === '!online'){
msg.channel.send(INIT_GAME.getOnline());
}
});
client.on('ready', () => {
console.log('Bot is now connected');
});
client.login(token);
[game.js]
class Game {
constructor(player, client, channelName='bot-testing', playersOnline){
this.client = client;
this.channelName = channelName;
this.currentPlayer = player;
this.playersOnline = [];
this.hitpoints = 120;
this.damage = '';
this.chance = 3;
this.inBattle = false;
this.online = playersOnline;
this.monster = [{
hp: Math.floor(Math.random() * 200),
temphp: 0,
damage: 10
}];
};
/* main menu information, players online */
startGame(){
for(var x = 0; x < this.currentPlayer.length; x++){
this.playersOnline.push(this.currentPlayer[x]);
if(this.playersOnline[x] === this.currentPlayer[x]){
return [`Players Online: ${this.online}\n`];
}
}
}
/* Battle system */
initBattle(currPlayer){
this.inBattle = true;
let npcHP = this.monster[0].hp;
let numberOfAttacks = 0;
let totalDamage=0, totalBonusDamage=0;
while( this.monster[0].hp > 0 ){
let playerDamage = Math.floor(Math.random() * (npcHP / 4));
if(this.bonusAttack() === 2){
console.log(`Bonus Attack: ${this.bonusAttack()}`);
console.log(`Regular damage without bonus attack: ${playerDamage}`);
playerDamage = playerDamage + 2;
}
this.monster[0].hp -= playerDamage;
this.hitpoints -= this.monster[0].damage;
console.log('Monster: ' + this.monster[0].hp);
console.log('Player: ' + this.hitpoints);
console.log(`${currPlayer} has attacked for ${playerDamage}`);
console.log(`NPC health: ${this.monster[0].hp}`);
if(this.hitpoints <= 0){
return [`You lost the battle.`];
}
this.inBattle = false;
numberOfAttacks++;
totalDamage += playerDamage;
totalBonusDamage = playerDamage + this.bonusAttack();
}
if(this.monster[0].hp <= 0 && this.inBattle !== true){
let maxDamage = totalDamage + totalBonusDamage;
return [`${currPlayer} has attacked ${numberOfAttacks} times dealing ${totalDamage} + (${totalBonusDamage}) bonus damage for a total of ${maxDamage} damage. The monster is dead.\n
Your Health: ${this.hitpoints}`];
}
else{
this.newGame();
return [`You rejuvenated your hitpoints and are ready for battle. \nType !fight again to start a new battle!`];
}
}
/* bonus attack damage [ 1 in 3 chance ] */
bonusAttack(bonusDamage){
let chance = Math.floor(Math.random() * 3);
return chance === 2 ? bonusDamage = 2 : false;
}
/* displays players currently online */
getOnline(){
console.log(this.currentPlayer);
return this.currentPlayer;
}
/* refresh stats */
newGame(){
this.monster[0].hp = Math.floor(Math.random() * 50);
this.hitpoints = 150;
}
}
module.exports = Game;
[My Question]
The only real important part within those 2 files is in index.js at the line that speaks about when a player leaves. So !leave.
I had a problem where one player typed !leave, both people would leave. That is the solution I used to fix it.
I could not get it to empty the array for ONLY the person who typed the command.
Example:
Person A types !join
Players online = [PlayerA]
Person B types !join
Players online = [PlayerA, PlayerB]
Player A types !leave
Players online = [[], PlayerB]]
It would always insert a empty array in the spot. So what I did was just fill that spot with the users previous name and their array id.
What I want is so that it completely deletes the person from the array AND removes that empty spot.
I would also like to know if it's possible that every time someone types !join, I would be able to insert them into an new array that is multidimensional and has the id for each player so that when I type !online, it would display
[[0, PlayerA], [1, PlayerB]] . Like a database where I can always see their index if needed.
What I have so far:
https://i.imgur.com/lWrtEtB.png
It only tracks the last index after they leave. How do I make it display the current index of the players online?
Use findIndex() to find the position of the name in your array. Then use the splice() method to remove a value from your array. You don't need to use that for loop, as findIndex will run a similar loop.
var playerIndex = playersName.findIndex(function(index) {
return index === tempLeave
})
playersName.splice(playerIndex, 1)
After reading the second part of your question though, I think you should be creating objects inside of your array. An example would be:
[
{
playerName: "Foo",
id: indexNumber,
isOnline: true
},
{
playerName: "Bar",
id: indexNumber,
isOnline: true
}
]
When someone joins, you check if their name is already assigned to an object (you can use findIndex again here). If not you create a new player, else you will change the players isOnline attribute to true. I'm sure this is the best way to go about storing user info, but it might work for you.
I created a tic tac toe game with JavaScript and took a purely functional approach. However, now I am stuck because I can't seem to be able to make the player be able to choose between x and o. The player is automatically assigned x, but never o. I'm using bootstrap, and I have a modal that appears when the user lands on the page. I have to fulfill a user story in which the user can choose between x and o via the modal buttons. Then the opposite symbol, ie o, would be on the tic tac toe board when the modal is dismissed. That part works fine right now. HOWEVER, if the user is to choose o, and x appears on the board, when the user clicks on a square, another x appears. I don't want an x to appear. An o should appear instead. So basically the user is playing against the board (or you could call it computer if you like), but is able to choose whether he/she wants to play as x or o by pressing on either the x or o button in the modal. Is there any way of making this possible without having to create an object.prototype.constructor? The link to my github repo: https://interglobalmedia.github.io/xoApp/ - sorry for the initial dead link- now it works!
function fetchBoard() {
var board = ['', '', '', '', '', '', '', '', ''];
$(".game-field").each(function (index) {
board[index] = $(this).html();
});
return board;
}
/* return 1 if all params 'x', -1 if o, and otherwise 0 */
function checkRow(a, b, c) {
if (a === 'x' && b === 'x' && c === 'x') {
return 1;
} else if (a === 'o' && b === 'o' && c === 'o') {
return -1;
} else {
return 0;
}
}
function checkWin(board) {
return checkRow(board[0], board[1], board[2]) // rows
+ checkRow(board[3], board[4], board[5])
+ checkRow(board[6], board[7], board[8])
+ checkRow(board[0], board[3], board[6]) // columns
+ checkRow(board[1], board[4], board[7])
+ checkRow(board[2], board[5], board[8])
+ checkRow(board[0], board[4], board[8]) // main diagonal rows
+ checkRow(board[2], board[4], board[6]) // reverse diagonal row
}
function selectMove(board) {
var i, options;
options = [];
for (i = 0; i < 9; i += 1) {
if (board[i] === '') {
options.push(i);
}
}
if (options.length === 0) {
return -1;
} else {
return options[Math.floor(Math.random() * options.length)];
}
}
function showGameOver(result) {
var target = $("#result");
if (result > 0) {
target.css('color', '#db0000');
target.html("you win!");
} else if (result < 0) {
target.css('color', "#db0000");
target.html("i win!");
} else {
target.css('color', '#db0000');
target.html("tie game.");
}
}
function resetGame() {
var target;
$(".game-field").html('');
target = $("#result");
target.css('color', '#db0000');
target.html('click a square');
}
function moveAt() {
var xCell, board, result, oLocation, oCell;
xCell = $(this);
if (xCell.html() !== '' || checkWin(fetchBoard()) !== 0) {
return;
}
xCell.css('color', '#db0000');
xCell.html('x');
board = fetchBoard();
result = checkWin(board);
if (result !== 0) {
showGameOver(result);
return;
}
oLocation = selectMove(board);
if (oLocation < 0) {
showGameOver();
return;
}
board[oLocation] = 'o';
oCell = $('#cell' + oLocation);
oCell.css('color', '#fff');
oCell.html('o');
result = checkWin(board);
if (result !== 0) {
showGameOver(result);
return;
}
}
$(document).ready(function() {
$('#iconModal').modal('show');
$('.game-field').click(moveAt);
$('#tictacreset').click(resetGame);
resetGame();
});
[1]: https://github.com/interglobalmedia/xoApp/tree/master/src/html
now I am stuck because I can't seem to be able to make the player be able to choose between x and o. The player is automatically assigned x, but never o.
How are the players assigned? Is this just meant to be played locally on the same browser? When the mouse is clicked, a player's turn is over, and then it is the next player's turn?
Is there any way of making this possible without having to create an object.prototype.constructor?
I might recommend you look at the Kestrel ('constant') combinator
const K = x => y => x
const p1 = K('x')
const p2 = K('o')
console.log(p1(), p2(), p1(), p2())
// x o x o
with K, you can pass any value in and all subsequent calls to the resulting function will result in the initial value. If you wanted to associate any other data with a player, you could use K(['x', otherData]) and continue building selectors as needed.
Or you could define the player using a lambda, I've chosen two fields here, but there's practically no limit on how many you could implement in this way. Add as many as you'd like ^_^
const player = (name,marker) => p => p(name,marker)
const playerName = p => p((name,marker) => name)
const playerMarker = p => p((name,marker) => marker)
const p1 = player('Bob', 'x')
const p2 = player('Alice', 'o')
console.log(playerName(p1), playerMarker(p1))
// Bob x
console.log(playerName(p2), playerMarker(p2))
// Alice o
I hope I understood the intent of your question and that the information provided here is helpful in some way. If not, please let me know. I'm happy to helper further.