Related
I'm trying to implement a Minesweeper game with the Undo feature. I used Stack data structure, the stack will push the tile that has been clicked on and pop it out when I clicked the Undo button. With the game Minesweeper, when I undo on a Tiled that has 0 value, it supposes to not only hide the opened tiled clicked on, but also the adjacent tiles that may have the same value 0. But it only could close the Tile has value 0 that I have clicked on, not all surrounding tiles like so.
I have attached the javascript code below on the function to pass in the Tiled that the stack pop() out.
const TILE_STATUSES = {
HIDDEN: "hidden",
MINE: "mine",
NUMBER: "number",
MARKED: "marked",
}
function createBoard(boardSize, numberOfMines) {
const board = []
const minePositions = getMinePositions(boardSize, numberOfMines)
for (let x = 0; x < boardSize; x++) {
const row = []
for (let y = 0; y < boardSize; y++) {
const element = document.createElement("div")
element.dataset.status = TILE_STATUSES.HIDDEN
const tile = {
element,
x,
y,
mine: minePositions.some(positionMatch.bind(null, { x, y })),
get status() {
return this.element.dataset.status
},
set status(value) {
this.element.dataset.status = value
},
}
row.push(tile)
}
board.push(row)
}
return board
}
function markTile(tile) {
if (
tile.status !== TILE_STATUSES.HIDDEN &&
tile.status !== TILE_STATUSES.MARKED
) {
return
}
if (tile.status === TILE_STATUSES.MARKED) {
tile.status = TILE_STATUSES.HIDDEN
} else {
tile.status = TILE_STATUSES.MARKED
}
}
function revealTile(board,tile){
if (tile.status !== TILE_STATUSES.HIDDEN) {
return
}
if (tile.mine) {
tile.status = TILE_STATUSES.MINE
return
}
tile.status = TILE_STATUSES.NUMBER
const adjacentTiles = nearbyTiles(board, tile)
const mines = adjacentTiles.filter(t => t.mine)
if (mines.length === 0) {
adjacentTiles.forEach(revealTile.bind(null, board))
} else {
tile.element.textContent = mines.length
}
}
function changeTile(board,tile){
if (tile.status !== TILE_STATUSES.HIDDEN) {
return
}
if (tile.mine) {
tile.status = TILE_STATUSES.MINE
return
}
tile.status = TILE_STATUSES.NUMBER
const adjacentTiles = nearbyTiles(board, tile)
const mines = adjacentTiles.filter(t => t.mine)
if (mines.length === 0) {
adjacentTiles.forEach(changeTile.bind(null, board))
} else {
tile.element.textContent = mines.length
}
return adjacentTiles
}
function checkWin(board) {
return board.every(row => {
return row.every(tile => {
return (
tile.status === TILE_STATUSES.NUMBER ||
(tile.mine &&
(tile.status === TILE_STATUSES.HIDDEN ||
tile.status === TILE_STATUSES.MARKED))
)
})
})
}
function checkLose(board) {
return board.some(row => {
return row.some(tile => {
return tile.status === TILE_STATUSES.MINE
})
})
}
function remarkTile(board,tile){
if(tile.status === TILE_STATUSES.HIDDEN){
return
}
else if(tile.status === TILE_STATUSES.MINE){
tile.status === TILE_STATUSES.HIDDEN
}
else if(tile.status === TILE_STATUSES.NUMBER ){
if(tile.element.textContent === ''){
console.log('j')
}
tile.element.textContent = null
}
tile.status = TILE_STATUSES.HIDDEN
}
function nearbyTiles(board, { x, y }) {
const tiles = []
for (let xOffset = -1; xOffset <= 1; xOffset++) {
for (let yOffset = -1; yOffset <= 1; yOffset++) {
const tile = board[x + xOffset]?.[y + yOffset]
if (tile) tiles.push(tile)
}
}
return tiles
}
function getMinePositions(boardSize, numberOfMines) {
const positions = []
while (positions.length < numberOfMines) {
const position = {
x: randomNumber(boardSize),
y: randomNumber(boardSize),
}
if (!positions.some(positionMatch.bind(null, position))) {
positions.push(position)
}
}
return positions
}
function positionMatch(a, b) {
return a.x === b.x && a.y === b.y
}
function randomNumber(size) {
return Math.floor(Math.random() * size)
}
<body>
<h3 class="title">Minesweeper</h3>
<div class="subtext">
Mines Left: <span data-mine-count></span>
</div>
<button id="undo"> Undo </button>
<div class="board"></div>
</body>
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
$("#threeToThree").click(function() {
playSound("Start");
executeTimer();
var nextColor = arrangeThreeToThreeColors();
$("#grid .btn").click(function() {
animatePress(this);
if ($(this).attr("id") === nextColor) {
playSound("Success");
totalPoint += 10;
console.log("Total Point: " + totalPoint);
seenColors = [];
nextColor = arrangeThreeToThreeColors();
} else {
playSound("Failure");
}
});
});
$("#fourToFour").click(function() {
playSound("Start");
executeTimer();
var nextColor = arrangeFourToFourColors();
$("#grid .btn").click(function() {
animatePress(this);
if ($(this).attr("id") === nextColor) {
playSound("Success");
totalPoint += 10;
console.log("Total Point: " + totalPoint);
seenColors = [];
nextColor = arrangeFourToFourColors();
} else {
playSound("Failure");
}
});
});
$("#fiveToFive").click(function() {
playSound("Start");
executeTimer();
var nextColor = arrangeFiveToFiveColors();
$("#grid .btn").click(function() {
animatePress(this);
if ($(this).attr("id") === nextColor) {
playSound("Success");
totalPoint += 10;
console.log("Total Point: " + totalPoint);
seenColors = [];
nextColor = arrangeFiveToFiveColors();
} else {
playSound("Failure");
}
});
});
function arrangeThreeToThreeColors() {
for (let i = 0; i <= 12; i++) {
let mod = i % 5;
if (mod === 0 || mod === 1 || mod === 2) {
var randomColor = getValidColor();
manipulateButtons(i, randomColor);
seenColors.push(randomColor);
} else {
continue;
}
}
var nextColor = getRandomColor(seenColors);
$("#next").attr("class", "btn next " + nextColor);
return nextColor;
}
function arrangeFourToFourColors() {
for (let i = 0; i <= 18; i++) {
let mod = i % 5;
if (mod === 0 || mod === 1 || mod === 2 || mod === 3) {
var randomColor = getValidColor();
manipulateButtons(i, randomColor);
seenColors.push(randomColor);
} else {
continue;
}
}
var nextColor = getRandomColor(seenColors);
$("#next").attr("class", "btn next " + nextColor);
return nextColor;
}
function arrangeFiveToFiveColors() {
for (let i = 0; i <= 24; i++) {
var randomColor = getValidColor();
manipulateButtons(i, randomColor);
seenColors.push(randomColor);
}
var nextColor = getRandomColor(seenColors);
$("#next").attr("class", "btn next " + nextColor);
return nextColor;
}
How can I make this code clear code? What is the best way to make this code more readable? Is there a way to implement #threeToThree.click(), #fourToFour.click() and #fiveToFive.click() contents in only one method? And can I combine the contents of arrangeThreeToThreeColors(), arrangeFourToFourColors(), arrangeFiveToFiveColors() in one method?
Added Comments along the code update, take what part of the code refactor best suits you and you can ignore the reset or modify as you want.
1. Using Constants insted of string
const SoundStatus = {
Start: "Start",
Success: "Success",
Failure: "Failure"
};
2. Global variables and using single point of update.
Single point update, would seem too much for simple update. But this will helpful when debugging, as you would know where the variables gets updated.
/**
* I couldn't get the context of these 2 variables; assuming them to be global variables
*/
let seenColors = [];
let totalPoint = 0;
/**
* Using Single setter to update global variables; With this it will be easier to track there changes
*/
function UpdateTotalPoint() {
totalPoint += 10;
}
function ResetSeenColors() {
seenColors = [];
}
function UpdateSeenColors(color) {
seenColors.push(color);
}
3. Removing Duplicate code
/**
* Abstract attribute update to single function
*/
function updateNextAttribute() {
let nextColor = getRandomColor(seenColors);
$("#next").attr("class", "btn next " + nextColor);
return nextColor;
}
function ManipulateButtonAndUpdateSeenColorsWithRandomValue(value) {
const randomColor = getValidColor();
manipulateButtons(value, randomColor);
UpdateSeenColors(randomColor);
}
/**
* Remove contiune on else, as there is no statements to be executed of that; Its understood
*/
function arrangeFiveToFiveColors() {
for (let i = 0; i <= 24; i++) {
ManipulateButtonAndUpdateSeenColorsWithRandomValue(i);
}
return updateNextAttribute();
}
function arrangeFourToFourColors() {
for (let i = 0; i <= 18; i++) {
let mod = i % 5;
if (mod === 0 || mod === 1 || mod === 2 || mod === 3) {
ManipulateButtonAndUpdateSeenColorsWithRandomValue(i);
}
}
return updateNextAttribute();
}
function arrangeThreeToThreeColors() {
for (let i = 0; i <= 12; i++) {
let mod = i % 5;
if (mod === 0 || mod === 1 || mod === 2) {
ManipulateButtonAndUpdateSeenColorsWithRandomValue(i);
}
}
return updateNextAttribute();
}
/**
* Grid Updation
* arrangeColor; will be arrangeThreeToThreeColors, arrangeFourToFourColors or arrangeFiveToFiveColors
*
*/
function AnimateGridButton(nextColor, arrangeColor, context) {
$("#grid .btn").click(function () {
animatePress(context);
if ($(context).attr("id") === nextColor) {
playSound(SoundStatus.Success);
UpdateTotalPoint();
console.log("Total Point: " + totalPoint);
ResetSeenColors();
nextColor = arrangeColor();
} else {
playSound(SoundStatus.Failure);
}
});
}
/**
* Updating click listiner of 3-3, 4-4 & 5-5
*/
$("#threeToThree").click(function () {
initilize();
const nextColor = arrangeThreeToThreeColors();
AnimateGridButton(nextColor, arrangeThreeToThreeColors, this);
});
$("#fourToFour").click(function () {
initilize();
const nextColor = arrangeFourToFourColors();
AnimateGridButton(nextColor, arrangeFourToFourColors, this);
});
$("#fiveToFive").click(function () {
initilize();
const nextColor = arrangeFiveToFiveColors();
AnimateGridButton(nextColor, arrangeFiveToFiveColors, this);
});
/**
* Abstracting inital Start sound and executeTimer
*/
function initilize() {
playSound(SoundStatus.Start);
executeTimer();
}
Final code after Refactoring
const SoundStatus = {
Start: "Start",
Success: "Success",
Failure: "Failure"
};
let seenColors = [];
let totalPoint = 0;
function updateTotalPoint() {
totalPoint += 10;
}
function resetSeenColors() {
seenColors = [];
}
function updateSeenColors(color) {
seenColors.push(color);
}
function updateNextAttribute() {
let nextColor = getRandomColor(seenColors);
$("#next").attr("class", "btn next " + nextColor);
return nextColor;
}
function ManipulateButtonAndUpdateSeenColorsWithRandomValue(value) {
const randomColor = getValidColor();
manipulateButtons(value, randomColor);
updateSeenColors(randomColor);
}
function arrangeFiveToFiveColors() {
for (let i = 0; i <= 24; i++) {
ManipulateButtonAndUpdateSeenColorsWithRandomValue(i);
}
return updateNextAttribute();
}
function arrangeFourToFourColors() {
for (let i = 0; i <= 18; i++) {
let mod = i % 5;
if (mod === 0 || mod === 1 || mod === 2 || mod === 3) {
ManipulateButtonAndUpdateSeenColorsWithRandomValue(i);
}
}
return updateNextAttribute();
}
function arrangeThreeToThreeColors() {
for (let i = 0; i <= 12; i++) {
let mod = i % 5;
if (mod === 0 || mod === 1 || mod === 2) {
ManipulateButtonAndUpdateSeenColorsWithRandomValue(i);
}
}
return updateNextAttribute();
}
function AnimateGridButton(nextColor, arrangeColor, context) {
$("#grid .btn").click(function () {
animatePress(context);
if ($(context).attr("id") === nextColor) {
playSound(SoundStatus.Success);
updateTotalPoint();
console.log("Total Point: " + totalPoint);
resetSeenColors();
nextColor = arrangeColor();
} else {
playSound(SoundStatus.Failure);
}
});
}
$("#threeToThree").click(function () {
initilize();
const nextColor = arrangeThreeToThreeColors();
AnimateGridButton(nextColor, arrangeThreeToThreeColors, this);
});
$("#fourToFour").click(function () {
initilize();
const nextColor = arrangeFourToFourColors();
AnimateGridButton(nextColor, arrangeFourToFourColors, this);
});
$("#fiveToFive").click(function () {
initilize();
const nextColor = arrangeFiveToFiveColors();
AnimateGridButton(nextColor, arrangeFiveToFiveColors, this);
});
function initilize() {
playSound(SoundStatus.Start);
executeTimer();
}
After all the possible abstraction, we are left with code that is specific for each functions; except for ManipulateButtonAndUpdateSeenColorsWithRandomValue (Please come up with a proper name, that would fit the requirment). Couple of lines of abstraction is still possible for arrageColor related functions. Since I don't have a full picture of your requirement, I would leave that to you.
I made a custom game in discord. It grabs words from a list of words in the scramble-words.json file and scrambles them. Those words get put into a text chat and the user playing the game has to try and find what the actual word is. It'll delete all new messages that get sent since you started the game and keep the channel clean as long as the game is active. You have to type the word and if it's correct you get a point.
The first time the command is run the code runs fine. The second time it gives an error
UnhandledPromiseRejectionWarning: DiscordAPIError: Unknown Message with every message you send in the new game.
I've tried looking up if it had to do something with me assigning the games = {} variable as a const but I don't think that's the problem and I can't think of what else it could be.
const { words } = require('./scramble-words.json');
const Discord = require('discord.js');
/*const example = {
channelId: {
message:'message object',
stage: 'string',
counter: 'number',
currentWord: 'string',
remainingWords: ['words here'],
points: {
userId: 'points'
}
}
}*/
const games = {}
const stages = {
'STARTING': (counter, topic) => {
return `-----------------------------**Game**------------------------------\nA new "${topic} Scramble" game is starting in ${counter}s!`
},
'IN_GAME': (word, topic) => {
let scrambledWord = '';
if(topic === "Brawlhalla"){
topic = "Brawlhalla related thing"
}
scrambledWord = shuffle(word);
return `-----------------------------**Game**------------------------------\nWhat ${topic} is this?\n\n **${scrambledWord}**`
},
'ENDING': (points) => {
const sorted = Object.keys(points).sort((a, b) => {
return points[b] - points[a]
})
let embed = new Discord.MessageEmbed();
embed.setTitle('**POINTS**')
.setDescription(`**The game is now over! Here's how everyone did!**`);
var results = `\n`
var firstPlace = points[sorted[0]]
var secondPlace = null
var thirdPlace = null
var leftover = null
for(const key of sorted) {
var amount = points[key]
if(leftover){
results += `<#${key}> had ${amount} point${amount === 1 ? '' : 's'}\n`
} else if(thirdPlace) {
if(thirdPlace === amount) {
results += `:third_place: <#${key}> had ${amount} point${amount === 1 ? '' : 's'}\n`
} else {
results += `\n`
results += `<#${key}> had ${amount} point${amount === 1 ? '' : 's'}\n`
leftover = amount
}
} else if(secondPlace) {
if(secondPlace === amount) {
results += `:second_place: <#${key}> had ${amount} point${amount === 1 ? '' : 's'}\n`
} else {
results += `\n`
results += `:third_place: <#${key}> had ${amount} point${amount === 1 ? '' : 's'}\n`
thirdPlace = amount
}
} else if(firstPlace) {
if(firstPlace === amount) {
results += `:first_place: <#${key}> had ${amount} point${amount === 1 ? '' : 's'}\n`
} else {
results += `\n`
results += `:second_place: <#${key}> had ${amount} point${amount === 1 ? '' : 's'}\n`
secondPlace = amount
}
}
}
if(results === `\n`){
return embed.setTimestamp().addField(`**Nobody got points you losers**`, "🤡");
} else {
return embed.setTimestamp().addField(`**Here's how everyone did**`, results)
}
}
}
const selectWord = (game) => {
game.currentWord = game.remainingWords[Math.floor(Math.random() * game.remainingWords.length)]
const index = game.remainingWords.indexOf(game.currentWord);
game.remainingWords.splice(index, 1);
game.currentWord = game.currentWord.toLowerCase().charAt(0).toUpperCase() + game.currentWord.toLowerCase().substring(1);
}
const gameLoop = async () => {
for(const key in games) {
const game = games[key];
const { message, stage } = game;
const gameLength = 2.345
if(stage === 'STARTING') {
let string = stages[stage](game.counter, game.topic)
message.edit(string)
if(game.counter <= 0) {
game.stage = 'IN_GAME'
game.counter = 60 * gameLength
selectWord(game)
string = stages[game.stage](game.currentWord, game.topic)
message.edit(string)
}
} else if (stage === 'IN_GAME') {
if(game.counter <= 0) {
game.stage = 'ENDING'
if(game.topic === "Brawlhalla")
{
game.topic === "Brawlhalla related thing"
}
const string = `-----------------------------**Game**------------------------------\nThe last ${game.topic} was: ${game.currentWord}\n------------------------------**End**-------------------------------`
message.edit(string)
embed = stages[game.stage](game.points);
message.channel.send(embed)
await delete games[key]
await delete game
return
}
}
--game.counter
}
setTimeout(gameLoop, 1000)
}
module.exports = {
scramble(member, channel, content, guild, message, { reply }, client) {
/*if(!member.hasPermission('ADMINISTRATOR')){
reply(true, `You don't have permission to execute this command!`)
return;
}*/
if(channel.name !== "scramble-games" && channel.name !== "games"){
reply(true, `You can only do this command in a channel called "scramble-games" or "games"`);
return;
}
if(games[channel.id]){
return;
}
message.delete();
var chosenCategory= []
if(content[0]){
if(content[0].toLowerCase() === "pokemon"){
if(content[1]){
var numbers = content[1].split('')
if(!isNaN(content[1])){
for(var i = 0; i < numbers.length; i++){
if(numbers[i] === "1") {
chosenCategory = chosenCategory.concat(pokemonGeneration1)
} else if(numbers[i] === "2") {
chosenCategory = chosenCategory.concat(pokemonGeneration2)
} else if(numbers[i] === "3") {
chosenCategory = chosenCategory.concat(pokemonGeneration3)
} else if(numbers[i] === "4") {
chosenCategory = chosenCategory.concat(pokemonGeneration4)
} else if(numbers[i] === "5") {
chosenCategory = chosenCategory.concat(pokemonGeneration5)
} else if(numbers[i] === "6") {
chosenCategory = chosenCategory.concat(pokemonGeneration6)
} else if(numbers[i] === "7") {
chosenCategory = chosenCategory.concat(pokemonGeneration7)
} else if(numbers[i] === "8") {
chosenCategory = chosenCategory.concat(pokemonGeneration8)
} else {
reply(true, `The numbers you gave had a number that is higher than 8. There are only 8 gens. Try again but keep the numbers you give under 8`)
return;
}
}
} else {
reply(true, `If you want to specify gens you have to give numbers only! example: 1456 will give gens 1, 4, 5 and 6`)
return;
}
} else {
chosenCategory = chosenCategory.concat(pokemonGeneration1, pokemonGeneration2, pokemonGeneration3, pokemonGeneration4, pokemonGeneration5, pokemonGeneration6, pokemonGeneration7, pokemonGeneration8)
}
topics = "Pokemon"
} else if (content[0].toLowerCase() === "brawlhalla" || content[0].toLowerCase() === "brawl"){
chosenCategory = chosenCategory.concat(BrawlhallaLegends, BrawlhallaWeapons, BrawlhallaWords, BrawlhallaColors, BrawlhallaStances, BrawlhallaChests, BrawlhallaGamemodes)
topics = "Brawlhalla"
} else {
reply(true, `That's not a valid topic for this game!`);
return
}
} else {
chosenCategory = words
topics = "Random Word"
}
channel.send('Preparing Game...').then((message) => {
games[channel.id] = {
message,
stage: 'STARTING',
counter: 10,
remainingWords: [...chosenCategory],
points: {},
topic: topics
}
});
gameLoop(channel)
client.on('message', message => {
const { channel, content, member } = message;
const { id } = channel
const game = games[id];
if(game && game.currentWord && !member.user.bot) {
checkWord(game, member, message, content);
}
if (game && !message.author.bot) {
message.delete();
}
return;
});
}
}
function checkWord(game, member, message, content){
if(game.stage === 'IN_GAME' && content.toLowerCase() === game.currentWord.toLowerCase()) {
const owo = game.currentWord;
game.currentWord = null;
const seconds = 3;
const { points } = game
points[member.id] = points[member.id] || 0
message.reply(`You got it!\n The correct answer was: **${owo}**\n +1 point (${++points[member.id]} total)`).then(newMessage => {
newMessage.delete({
timeout: 1000 * seconds
});
});
setTimeout(() => {
if(game.stage === 'IN_GAME') {
selectWord(game)
const string = stages[game.stage](game.currentWord, game.topic)
game.message.edit(string);
}
}, 1000 * seconds);
}
}
function shuffle(word) {
var wordArray = word.split(' ');
var output = []
for(var i = 0; i < wordArray.length; i++) {
var array = wordArray[i].split('');
var currentIndex = array.length;
var temporaryValue;
var randomIndex;
// While there remain elements to shuffle...
while (0 !== currentIndex) {
// Pick a remaining element...
randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex -= 1;
// And swap it with the current element.
temporaryValue = array[currentIndex];
array[currentIndex] = array[randomIndex];
array[randomIndex] = temporaryValue;
}
output = output.concat(array)
if(!i + 1 === wordArray.length){
output.push(' ');
}
}
return output.join('');
}
Here's the json file too so you can try out the game yourself
{
"words": [
"Provincial",
"Grudge",
"Foundation",
"Carry",
"Fight",
"Belt",
"Perforate",
"Obstacle",
"Hide",
"Lesson",
"Car",
"Building",
"Mourning",
"Debut",
"Sunrise",
"Scatter",
"Clash",
"Equation",
"Performer",
"Ask",
"Butterfly",
"Medieval",
"Think",
"Minimum",
"Play",
"Sofa",
"Minority",
"Friend",
"Protect",
"Mess",
"Disability",
"Planet",
"Federation",
"Film",
"Vegetarian",
"Utter",
"Polish",
"Ankle",
"Calendar",
"Extreme",
"Student",
"Barrier",
"Motif",
"Solo",
"Toast",
"Steel",
"Speaker",
"Concede",
"Suit",
"gimping",
"Weal",
"Affords",
"Palship",
"Trefoil",
"Kirtled",
"Jaybirds",
"Shires",
"Gobbling",
"Puffery",
"Bag",
"Idoneous",
"Aerates",
"Kitchens",
"Zorils",
"Students",
"Idles",
"Howe",
"Veenas",
"Airship",
"Olefins",
"Munsters",
"Polemics",
"Lentos",
"Snog",
"Fusel",
"Giglot",
"Pinafore",
"Snowiest",
"Saxtuba",
"Division",
"Mantlet",
"Nurtures",
"Geoponic",
"Civvies",
"Trommels",
"Engraver",
"Know",
"Gummoses",
"Disbands",
"Parse",
"Donator",
"Minces",
"Lofted",
"Punters",
"Lie",
"Rune",
"Dottiest",
"Mib",
"Enwraps",
"Bizzes",
"Nitride",
"Ire",
"Bricole",
"Bigness",
"Roadeos",
"Midriffs",
"Fallers",
"Postcode",
"Sterigma",
"Duvetyne",
"Alumroot",
"Purins",
"Pricking",
"Deluders",
"Postcoup",
"Daggas",
"Rallies",
"Vocably",
"Gravida",
"Eluded",
"Dicyclic",
"Starers",
"Afflatus",
"Misbind",
"Coadmire",
"Overrule",
"Marquis",
"Pogromed",
"Dulled",
"Lantana",
"Garotte",
"Keek",
"Dhak",
"Mescals",
"Trichite",
"Theurgic",
"Cretins",
"Codicils",
"Depside",
"Cadres",
"Desium",
"Indene",
"Depicted",
"Opticist",
"Hoptoad",
"Jacking",
"Girasole",
"Wedeln",
"Pull"
]
}
Unknown message errors occur when you delete a message, then try and fetch that message, or use it in a method. I can see 2 message#delete methods in your code, and these is nearly always that cause of these errors. Go through your code and find where you are deleting the messages, and make sure you are not using anything to do with that message afterwards. The post here displays how these errors occur.
I can see one reason this error would throw, if you are iterating through the games, you use message#delete in the first game and the program runs fine, then in all of the other iterations it throws that error. This is because the message#delete is trying to delete an already deleted message (i.e. when you first deleted it in the first game).
I am trying to code an evolutionary neural network for the game snake. I already coded the neural network part and now I'd like to output the game of the best individual of every generation. For that I'm using the drawing library p5.js (https://p5js.org/).
In my code I am running a loop in where a new generation based on the last generation is created. Each individual of the generation will have to play the game and that is how they are rated. Now I want the best individual to be outputted after I let every individual play once.
Between each outputted turn of the game of the best individual I want the code to wait 500 milliseconds. How do I achieve that?
Here is the code I've already tried but here it only outputed the board after the last turn of each generation:
async function start() {
for (let i = 0; i < 50; i++) {
population.createNewGeneration();
let bestGameTurns = population.bestIndividual.game.turns; //Array of boards
for (let turn = 0; turn < bestGameTurns.length; turn++) {
let board = bestGameTurns[turn];
drawBoard(board);
let p = new Promise(resolve => setTimeout(resolve, 500));
await p;
function drawBoard(board) {
//Draw the board using p5.js rect()'s
}
}
}
}
Another version but the waiting didn't work here:
let i = 0;
setInterval(async () => {
population.createNewGeneration();
console.log(i, population.avgFitness);
let bestGameTurns = population.bestIndividual.game.turns; //Array of boards
for (let turn = 0; turn < bestGameTurns.length; turn++) {
let board = bestGameTurns[turn];
drawBoard(board);
let p = new Promise(resolve => setTimeout(resolve, 500));
await p;
function drawBoard(board) {
//Draw the board using p5.js rect()'s
}
}
i++;
}, 1);
The code you provided should do what you asked for, I could only clean up some parts for you. Explain a bit better what is the problem you are facing.
// The function should be defined only once.
function drawBoard(board) { }
async function start() {
for (let i = 0; i < 50; i++) {
population.createNewGeneration();
const bestGameTurns = population.bestIndividual.game.turns; //Array of boards
for (let turn = 0; turn < bestGameTurns.length; turn++) {
// Don't wait on first iteration
await new Promise(resolve => setTimeout(resolve, 500 * (turn ? 0 : 1 )));
drawBoard(bestGameTurns[turn]);
}
}
}
Original idea (discarded)
You can create a short function like that:
function pause(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
Then in any async function you can call it like that:
async function () {}
// something happening
await pause(500);
// continue
}
The other idea
Now, the code in your question is not complete so this is kind of blind coding but.
So, first of all setInterval will be running the whole function every 1 millisecond (actually 4 ms, as this is the minimum in JS). Which means it will run those loops. I decided to focus on the part that was marked by you.
Instead of loop and trying to pause it, I ask that maybe rewriting the loop into function called each half second until condition is met would do?
Also, I move drawBoard outside
setInterval(async () => {
// ^^^^^^^^ <-- this should probably go
population.createNewGeneration();
console.log(i, population.avgFitness);
let bestGameTurns = population.bestIndividual.game.turns; //Array of boards
function tick(turn = 0) {
let board = bestGameTurns[turn];
function drawBoard(board) {
//Draw the board using p5.js rect()'s
}
drawBoard(board);
// here is "setTimeouted" loop
if (turn < bestGameTurns.length) {
setTimeout(tick, 500, turn + 1);
}
}
tick();
}, 1);
Thanks to everyone, your suggestions brought me to an idea. I found out that the problem was lying somewhere else. Because javascript only runs on the one thread (I think thats how its called), after we run over one generation, we have to stop that function to let another draw function, which runs every frame, draw the board. After it is drawn, the main function can continue. This is how it looks:
let isDrawn = false;
let currentBoard;
async function setup() {
for (let i = 0; i < 50; i++) {
population.createNewGeneration();
const bestGameTurns = population.bestIndividual.game.turns;
for (let turn = 0; turn < bestGameTurns.length; turn++) {
await step(bestGameTurns[turn], turn);
}
}
}
function step(board, turn) {
currentBoard = board;
isDrawn = false;
return new Promise(resolve => setTimeout(() => {
if (isDrawn) resolve();
}, 500));
}
setTimeout(() => {
if (currentBoard) {
drawBoard(currentBoard);
isDrawn = true;
currentBoard = undefined;
}
}, 1);
const nrOfCols = 10;
const nrOfRows = 10;
const fieldWidth = 20;
const nodeNrs = [24, 8, 8, 4];
const populationSize = 200;
const mutationRate = 0.01;
let population;
let game;
let isDrawn = false;
let currentBoard;
async function setup() {
createCanvas(500, 500);
population = new PopulationManager(populationSize);
for (let i = 0; i < 50; i++) {
population.createNewGeneration();
const bestGameTurns = population.bestIndividual.game.turns;
for (let turn = 0; turn < bestGameTurns.length; turn++) {
await step(bestGameTurns[turn]);
}
}
}
function step(board) {
currentBoard = board;
isDrawn = false;
return new Promise(resolve => setTimeout(() => {
if (isDrawn) resolve();
}, 500));
}
function draw() {
if (currentBoard) {
drawBoard(currentBoard);
isDrawn = true;
currentBoard = undefined;
}
}
function drawBoard(board) {
board.forEach((col, colNr) => {
col.forEach((field, rowNr) => {
fill(field.isSnake ? "green" : field.isFruit ? "red" : "black");
stroke(255);
rect(colNr*fieldWidth, rowNr*fieldWidth, fieldWidth, fieldWidth);
});
});
}
function play(game) {
setInterval(() => {
if (!game.lost) {
game.nextTurn();
drawBoard(game.board);
} else {
clearInterval(1);
}
}, 500);
}
class PopulationManager {
constructor(populationSize) {
this.population = createPopulation();
function createPopulation() {
let population = [];
for (let i = 0; i < populationSize; i++) {
let chromosomes = createRandomChromosomes();
let i = new Individual(chromosomes);
population.push(i);
}
return population;
function createRandomChromosomes() {
let arr = [];
let nrOfChromosomes = calcNrOfChromosomes();
for (let i = 0; i < nrOfChromosomes; i++)
arr.push(Math.random()*2-1);
return arr;
function calcNrOfChromosomes() {
let nr = 0;
for (let i = 0; i < nodeNrs.length - 1; i++)
nr += (nodeNrs[i] + 1)*nodeNrs[i + 1];
return nr;
}
}
};
}
createNewGeneration() {
let that = this;
getFitnessOfPop();
this.calcAvgFitness();
this.findBestIndividual();
let parents = selection();
breed(parents);
function getFitnessOfPop() {
that.population.forEach(iv => {
iv.fitness = iv.playGame();
});
that.population.sort((a, b) => a.fitness - b.fitness);
}
function selection() {
let totalFitness = that.population.map(iv => iv.fitness/* + 1 */).reduce((a,b) => a + b);
let allParents = [];
for (let i = 0; i < that.population.length/2; i++) {
allParents.push(selectRandomParents());
}
return allParents;
function selectRandomParents() {
let p1, p2;
do {
p1 = selectRandomParent();
p2 = selectRandomParent();
} while (p1 == p2);
return [p1, p2];
function selectRandomParent() {
let rdm = Math.random()*totalFitness;
return that.population.find((iv, i) => {
let sum = that.population.filter((iv2, i2) => i2 <= i).map(iv => iv.fitness /* + 1 */).reduce((a,b) => a + b);
return rdm <= sum;
});
}
}
}
function breed(allParents) {
that.population = [];
allParents.forEach(ps => {
let childChromosomes = crossOver(ps[0].chromosome, ps[1].chromosome);
childChromosomes = [mutate(childChromosomes[0]), mutate(childChromosomes[1])];
let child1 = new Individual(childChromosomes[0]);
let child2 = new Individual(childChromosomes[1]);
that.population.push(child1);
that.population.push(child2);
});
function crossOver(parent1Chromosome, parent2Chromosome) {
let crossingPoint = Math.round(Math.random()*parent1Chromosome.length);
let divided1 = divideChromosome(parent1Chromosome);
let divided2 = divideChromosome(parent2Chromosome);
let child1Chromosome = divided1[0].concat(divided2[1]);
let child2Chromosome = divided2[0].concat(divided1[1]);
return [child1Chromosome, child2Chromosome];
function divideChromosome(chromosome) {
let part1 = chromosome.filter((g, i) => i <= crossingPoint);
let part2 = chromosome.filter((g, i) => i > crossingPoint);
return [part1, part2];
}
}
function mutate(chromosome) {
chromosome = chromosome.map(g => {
if (Math.random() < mutationRate)
return Math.random()*2-1;
return g;
});
return chromosome;
}
}
}
calcAvgFitness() {
this.avgFitness = this.population.map(iv => iv.fitness).reduce((a, b) => a + b) / this.population.length;
}
findBestIndividual() {
let bestFitness = -1, bestIndividual;
this.population.forEach(i => {
if (i.fitness > bestFitness) {
bestFitness = i.fitness;
bestIndividual = i;
}
});
this.bestIndividual = bestIndividual;
}
}
class Individual {
constructor(chromosome) {
this.chromosome = chromosome;
this.fitness = 0;
this.game = createGame();
function createGame() {
let weights = convertChromosomeToWeights();
let game = new Game(weights);
return game;
function convertChromosomeToWeights() {
let weights = [];
for (let i = 0; i < nodeNrs.length - 1; i++) {
let lArr = [];
for (let j = 0; j < nodeNrs[i] + 1; j++) {
let nArr = [];
lArr.push(nArr);
}
weights.push(lArr);
}
chromosome.forEach((gene, geneIdx) => {
let lIdx = -1, minIdx, maxIdx = 0;
for (let i = 0; i < nodeNrs.length - 1; i++) {
let nr = 0;
for (let j = 0; j <= i; j++)
nr += (nodeNrs[j] + 1)*nodeNrs[j + 1];
if (geneIdx < nr) {
lIdx = i;
break;
}
maxIdx = nr;
minIdx = maxIdx;
}
minIdx = maxIdx;
let nIdx = -1;
for (let i = 0; i < nodeNrs[lIdx] + 1; i++) {
let nr = minIdx + nodeNrs[lIdx + 1];;
if (geneIdx < nr) {
nIdx = i;
break;
}
maxIdx = nr;
minIdx = maxIdx;
}
minIdx = maxIdx;
let wIdx = -1;
for (let i = 0; i < nodeNrs[lIdx + 1]; i++) {
let nr = minIdx + 1;
if (geneIdx < nr) {
wIdx = i;
break;
}
maxIdx = nr;
minIdx = maxIdx;
}
weights[lIdx][nIdx][wIdx] = gene;
});
return weights;
}
}
}
playGame() {
while (!this.game.lost) {
this.game.nextTurn();
}
return this.game.score;
}
}
class Game {
constructor(weights) {
let that = this;
this.chromosome = flattenArray(weights);
this.nn = new NeuralNetwork(weights);
this.turnNr = 0;
this.score = 0;
this.lost = false;
this.board = createBoard();
this.snake = new Snake();
setupSnake();
this.createFruit();
this.turns = [JSON.parse(JSON.stringify(this.board))];
function createBoard() {
let board = [];
for (let colNr = 0; colNr < nrOfCols; colNr++) {
board[colNr] = [];
for (let rowNr = 0; rowNr < nrOfRows; rowNr++) {
let field = new Field(colNr, rowNr);
board[colNr][rowNr] = field;
}
}
return board;
}
function setupSnake() {
for (let i = 0; i < 4; i++)
that.addToTail([floor(nrOfCols/2) - i, floor(nrOfRows/2)]);
that.length = that.snake.body.length;
}
function flattenArray(arr) {
let flattened = [];
flatten(arr);
return flattened;
function flatten(arr) {
arr.forEach(e => {
if (Array.isArray(e))
flatten(e);
else
flattened.push(e);
});
}
}
}
addToTail(pos) {
this.snake.body.push(pos);
this.board[pos[0]][pos[1]].setSnake(true);
}
nextTurn() {
let that = this;
let direction = findDirection();
this.moveSnake(direction);
this.turns.push(JSON.parse(JSON.stringify(this.board)));
this.turnNr++;
checkEat();
function findDirection() {
let inputValues = [];
for (let i = 0; i < 8; i++) {
let distances = that.snake.look(i, that.board);
inputValues.push(distances.distToFruit);
inputValues.push(distances.distToWall);
inputValues.push(distances.distToBody);
}
let output = that.nn.getOutput(inputValues);
let probability = -1;
let direction = -1;
output.forEach((v, vIdx) => {
if (v > probability) {
probability = v;
direction = vIdx;
}
});
return direction;
}
function checkEat() {
let head = that.snake.body[0];
let headField = that.board[head[0]][head[1]];
if (headField.isFruit) {
that.snake.eat();
that.score++;
headField.setFruit(false);
that.createFruit();
}
}
}
createFruit() {
let field;
do {
let colNr = floor(random()*nrOfCols);
let rowNr = floor(random()*nrOfRows);
field = this.board[colNr][rowNr];
} while(field.isSnake);
field.setFruit(true);
}
moveSnake(newDirection) {
let that = this;
let oldBody = JSON.parse(JSON.stringify(that.snake.body));
moveTail();
makeSnakeLonger();
moveHead();
function moveTail() {
for (let i = oldBody.length - 1; i > 0; i--)
that.snake.body[i] = oldBody[i - 1];
}
function moveHead() {
let newHeadPosition = findNewHeadPos();
if (
newHeadPosition[0] >= nrOfCols || newHeadPosition[0] < 0 ||
newHeadPosition[1] >= nrOfRows || newHeadPosition[1] < 0
) {
that.lose();
return;
}
let newHeadField = that.board[newHeadPosition[0]][newHeadPosition[1]];
if (newHeadField.isSnake) {
that.lose();
return;
}
addNewHeadPos(newHeadPosition);
}
function findNewHeadPos() {
if (newDirection == 0) { //up
return [oldBody[0][0], oldBody[0][1] - 1];
} else if (newDirection == 1) { //right
return [oldBody[0][0] + 1, oldBody[0][1]];
} else if (newDirection == 2) { //down
return [oldBody[0][0], oldBody[0][1] + 1];
} else if (newDirection == 3) { //left
return [oldBody[0][0] - 1, oldBody[0][1]];
}
}
function makeSnakeLonger() {
if (that.snake.length > that.snake.body.length) {
that.addToTail(oldBody[oldBody.length - 1]);
} else {
removeFromTail(oldBody[oldBody.length - 1]);
}
}
function removeFromTail(pos) {
that.board[pos[0]][pos[1]].setSnake(false);
}
function addNewHeadPos(pos) {
that.snake.body[0] = pos;
that.board[pos[0]][pos[1]].setSnake(true);
}
}
lose() {
this.lost = true;
}
}
class Field {
constructor(col, row) {
this.col = col;
this.row = row;
this.isFruit = false;
this.isSnake = false;
}
setFruit(bool) {
this.isFruit = bool;
}
setSnake(bool) {
this.isSnake = bool;
}
}
class Snake {
constructor() {
this.body = [];
this.length = 4;
}
eat() {
this.length++;
}
look(direction, board) {
let distances = {distToFruit: 0, distToWall: 0, distToBody: 0};
let xDiff = getXDiff(direction), yDiff = getYDiff(direction);
let pos = [this.body[0][0] + xDiff, this.body[0][1] + yDiff];
let dist = 1;
while (pos[0] > 0 && pos[0] < nrOfRows && pos[1] > 0 && pos[1] < nrOfCols) {
if (board[pos[0]][pos[1]].isFruit && distances.distToFruit == 0) distances.distToFruit = dist;
if (board[pos[0]][pos[1]].isSnake && distances.distToBody == 0) distances.distToBody = dist;
pos[0] += xDiff, pos[1] += yDiff;
dist++;
}
distances.distToWall = dist;
return distances;
function getXDiff(direction) {
if (direction == 5 || direction == 6 || direction == 7) return -1;
else if (direction == 1 || direction == 2 || direction == 3) return 1;
return 0;
}
function getYDiff(direction) {
if (direction == 7 || direction == 0 || direction == 1) return -1;
else if (direction == 3 || direction == 4 || direction == 5) return 1;
return 0;
}
}
}
class NeuralNetwork {
constructor(weights) {
this.layers = createLayers();
this.layers = addWeights(this.layers, weights);
function createLayers() {
let layers = [];
let nrOfNodesGlobal;
nodeNrs.forEach((nrOfNodes, lNr) => {
nrOfNodesGlobal = nrOfNodes;
layers[lNr] = [];
for (let i = 0; i < nrOfNodes; i++) {
let node = createNode(lNr);
layers[lNr][i] = node;
}
if (lNr != nodeNrs.length - 1)
layers[lNr].push(new Bias());
});
return layers;
function createNode(lNr) {
if (lNr == 0) return new InputLayerNode();
else if (lNr == nrOfNodesGlobal - 1) return new OutputLayerNode();
else return new HiddenLayerNode();
}
}
function addWeights(layers, weights) {
for (let lNr = 0; lNr < layers.length - 1; lNr++) {
let l = layers[lNr];
l.forEach((n1, nNr) => {
for (let n2Nr = 0; n2Nr < layers[lNr+1].length - 1; n2Nr++) { //not including bias of next layer
let n2 = layers[lNr+1][n2Nr];
let weight = weights[lNr][nNr][n2Nr];
let w = new Weight(n1, n2, weight);
n1.addWeight(w);
}
});
}
return layers;
}
}
getOutput(inputValues) {
let output = [];
this.layers[0].forEach((inputNeuron, nNr) => {
if (nNr != this.layers[0].length - 1)
inputNeuron.addToInput(inputValues[nNr]);
});
this.layers.forEach((l, lNr) => {
calcOutputs(l);
if (lNr != this.layers.length - 1) {
l.forEach(n => {
n.feedForward();
});
} else {
output = l.map(n => n.output);
}
});
return output;
function calcOutputs(layer) {
layer.forEach(n => n.output = n.activationFunction(n.summedInput, layer.map(N => N.summedInput)));
}
}
log() {
console.log(this.weights, this.nodes);
}
}
class Node {
constructor() {
this.weights = [];
this.summedInput = 0;
}
addWeight(w) {
this.weights.push(w);
}
addToInput(input) {
if (input == NaN)
console.log("A");
this.summedInput += input;
}
feedForward() {
this.weights.forEach((w, wNr) => {
let input = w.weight*this.output;
w.to.addToInput(input);
});
}
}
class Bias extends Node {
constructor() {
super();
this.output = 1;
}
activationFunction(x, allXs) {
return x;
}
}
class InputLayerNode extends Node {
constructor() {
super();
}
activationFunction(x, allXs) {
return x;
}
}
class HiddenLayerNode extends Node {
constructor() {
super();
}
activationFunction(x, allXs) {
return leakyReLU(x);
}
}
class OutputLayerNode extends Node {
constructor() {
super();
}
activationFunction(x, allXs) {
return softmax(x, allXs);
}
}
class Weight {
constructor(from, to, weight) {
this.from = from;
this.to = to;
this.weight = weight;
}
setWeight(newWeight) {
this.weight = weight;
}
}
function leakyReLU(x) {
if (x >= 0) return x;
else return 0.01*x;
}
function softmax(x, allXs) {
return Math.exp(x) / allXs.map(X => Math.exp(X)).reduce((a, b) => a+b);
}
<!DOCTYPE html>
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/p5.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/addons/p5.dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.8.0/addons/p5.sound.min.js"></script>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8" />
</head>
<body>
<script src="sketch.js"></script>
</body>
</html>
It's not working that well but a few improvements should make it better...
If you have any suggestions for improvements of the code, please let me know!
I tried to fix it into steps as I said in comments,I hope I have no mistake :
let i = 0;
async function step(bestGameTurns, turn)
{
if (turn == bestGameTurns.length)
return;
let board = bestGameTurns[turn];
drawBoard(board);
let p = new Promise(resolve => setTimeout(() => step(bestGameTurns, turn+1), 500));
await p;
}
function drawBoard(board) {
//Draw the board using p5.js rect()'s
}
setInterval(async () => {
population.createNewGeneration();
console.log(i, population.avgFitness);
let bestGameTurns = population.bestIndividual.game.turns; //Array of boards
step(bestGameTurns, 0);
i++;
}, 1);
I am a university student currently doing a project on a game on AS3
When i run my game i get 8 errors regarding "1120: Access of undefined property...." I don't know what I'm doing wrong so can anyone help me with this?
here is my code(yeah i know its long):
package {
import flash.display.*;
import flash.events.*;
import flash.text.*;
import flash.utils.getTimer;
public class PlatformGamess extends MovieClip {
// movement constants
static const gravity:Number=.004;
// screen constants
static const edgeDistance:Number=100;
// object arrays
private var fixedObjects:Array;
private var otherObjects:Array;
// hero and enemies
private var hero:Object;
private var enemies:Array;
// game state
private var playerObjects:Array;
private var gameScore:int;
private var gameMode:String="start";
private var playerLives:int;
private var lastTime:Number=0;
// start game
public function startPlatformGamess(){
playerObjects=new Array();
gameScore=0;
gameMode="play";
playerLives=3;
}
public function startGameLevel(){
createHero();
addEnemies();
examineLevel();
this.addEventListener(Event.ENTER_FRAME,gameLoop);
stage.addEventListener(KeyboardEvent.KEY_DOWN,keyDownFunction);
stage.addEventListener(KeyboardEvent.KEY_UP,keyUpFunction);
gameMode="play";
addScore(0);
showLives();
}
// creates the hero object and sets all properties
public function createHero() {
hero=new Object();
hero.mc=gamelevel.hero;
hero.inAir=false;
hero.direction=1;
hero.animstate="stand";
hero.walkAnimation=new Array(2)
hero.animstep=0;
hero.jump=false;
hero.moveLeft=false;
hero.moveRight=false;
hero.jumpSpeed=.8;
hero.walkSpeed=.15;
hero.width=20.0;
hero.height=40.0;
hero.startx=hero.mc.x;
hero.starty=hero.mc.y;
}
public function addEnemies() {
enemies = new Array();
var i:int=1;
while (true) {
if (gamelevel["enemy"+i]==null) break;
var enemy=new Object();
enemy.mc=gamelevel["enemy"+i];
enemy.dx=0.0;
enemy.dy=0.0;
enemy.inAir=false;
enemy.direction=1;
enemy.animstate="stand"
enemy.walkAnimation = new Array(2);
enemy.animstep = 0;
enemy.jump = false;
enemy.moveRight = true;
enemy.moveLeft = false;
enemy.jumpSpeed = 1.0;
enemy.walkSpeed = .08;
enemy.width = 30.0;
enemy.height = 30.0;
enemies.push(enemy);
i++;
}
}
public function examineLevel(){
fixedObjects=new Array();
otherObject=new Array();
for (var i:int=0;i<this.gamelevel.numChildren;i++) {
var mc=this.gamelevel.getChildAt(i);
if ((mc is Floor) || (mc is Wall)) {
var floorObjects:Object=new Object();
floorObject.mc=mc;
floorObject.leftside=mc.x;
floorObject.rightside=mc.x+mc.width;
floorObject.topside.mc.y;
floorObject.bottomside=mc.y+mc.height;
fixedObjects.push(floorObject);
} else if ((mc is Treasure) || (mc is Key) ||
(mc is Door) || (mc is Chest)) {
otherObjects.push(mc);
}
}
}
public function keyDownFunction(event:KeyboardEvent) {
if (gamemode != "play") return; // don't move until in play mode
if (event.keyCode==37){
hero.moveLeft=true;
} else if (event.keyCode==39) {
hero.moveRight==true;
} else if (event.keyCode==32){
if (!hero.inAir){
hero.jump=true;
}
}
}
public function keyUpFunction(event:KeyboardEvent) {
if (event.keyCode == 37) {
hero.moveLeft = false;
} else if (event.keyCode == 39) {
hero.moveRight = false;
}
}
public function gameLoop(event:Event) {
if (lastTime==0)lastTime=getTimer();
var timeDiff:int=getTimer()-lastTime;
lastTime+=timeDiff;
if (gameMode=="play"){
moveCharacter(hero,timeDiff);
moveEnemies(timeDiff);
checkCollisions();
scrollWithHero();
}
}
// loop through all enemies and move them
public function moveEnemies(timeDiff:int) {
for(var i:int=0;i<enemies.length;i++) {
// move
moveCharacter(enemies[i],timeDiff);
// if hit a wall, turn around
if (enemies[i].hitWallRight) {
enemies[i].moveLeft=true;
enemies[i].moveRight=false;
} else if (enemies[i].hitWallLeft) {
enemies[i].moveLeft=false;
enemies[i].moveRight=true;
}
}
}
public function moveCharacter(char:Object,timeDiff:Number) {
if (timeDiff < 1) return;
var verticalChange:Number=char.dy*timeDiff+timeDiff*gravity;
if (verticalChange>15.0) verticalChange=15.0;
char.dy+=timeDiff*gravity;
var horizontalChange=0;
var newAnimState:String="stand";
var newDirection:int=char.direction;
if (char.moveLeft){
// wa
horizontalChange = -char.walkSpeed*timeDiff;
newAnimState="walk";
newDirection=-1;
} else if (char.moveRight){
// walk right
horizontalChange = char.walkSpeed*timeDiff;
newAnimState="walk";
newDirection=1;
}
if (char.jump) {
// start jump
char.jump = false;
char.dy = -char.jumpSpeed;
verticalChange = -char.jumpSpeed;
newAnimState = "jump";
}
// assume no wall hit, and hanging in air
char.hitWallRight = false;
char.hitWallLeft = false;
char.inAir = true;
// find new vertical position
var newY:Number = char.mc.y + verticalChange;
// loop through all fixed objects to see if character has landed
for(var i:int=0;i<fixedObjects.length;i++) {
if ((char.mc.x+char.width/2 > fixedObjects[i].leftside) && (char.mc.x-char.width/2 < fixedObjects[i].rightside)) {
if ((char.mc.y <= fixedObjects[i].topside) && (newY > fixedObjects[i].topside)) {
newY = fixedObjects[i].topside;
char.dy = 0;
char.inAir = false;
break;
}
}
}
// find new horizontal position
var newX:Number = char.mc.x + horizontalChange;
// loop through all objects to see if character has bumped into a wall
for(i=0;i<fixedObjects.length;i++) {
if ((newY > fixedObjects[i].topside) && (newY-char.height < fixedObjects[i].bottomside)) {
if ((char.mc.x-char.width/2 >= fixedObjects[i].rightside) && (newX-char.width/2 <= fixedObjects[i].rightside)) {
newX = fixedObjects[i].rightside+char.width/2;
char.hitWallLeft = true;
break;
}
if ((char.mc.x+char.width/2 <= fixedObjects[i].leftside) && (newX+char.width/2 >= fixedObjects[i].leftside)) {
newX = fixedObjects[i].leftside-char.width/2;
char.hitWallRight = true;
break;
}
}
}
// set position of character
char.mc.x = newX;
char.mc.y = newY;
// set animation state
if (char.inAir) {
newAnimState = "jump";
}
char.animstate = newAnimState;
// move along walk cycle
if (char.animstate == "walk") {
char.animstep += timeDiff/60;
if (char.animstep > char.walkAnimation.length) {
char.animstep = 0;
}
char.mc.gotoAndStop(char.walkAnimation[Math.floor(char.animstep)]);
// not walking, show stand or jump state
} else {
char.mc.gotoAndStop(char.animstate);
}
// changed directions
if (newDirection != char.direction) {
char.direction = newDirection;
char.mc.scaleX = char.direction;
}
}
public function scrollWithHero(){
var stagePosition:Number = gamelevel.x+hero.mc.x;
var rightEdge:Number = stage.stageWidth-edgeDistance;
var leftEdge:Number = edgeDistance;
if (stagePosition > rightEdge){
gamelevel.x -= (stagePosition-rightEdge);
if (gamelevel.x < -(gamelevel.width-stage.stageWidth)) gamelevel.x = -(gamelevel.width-stage.stageWidth);
}
if (stagePosition < leftEdge){
gamelevel.x += (leftEdge-stagePosition);
if (gamelevel.x > 0)gamelevel.x=0;
}
}
public function checkCollisions(){
// enemies
for(var i:int=enemies.length-1;i>=0;i--){
if (hero.mc.hitTestObject(enemies[i].mc)){
if (hero.inAir && (hero.dy>0)){
enemyDie(i);
} else {
heroDie();
}
}
}
for(i=otherObjects.length-1;i>=0;i--) {
if (hero.mc.hitTestObject(otherObjects[i])) {
getObject(i);
}
}
}
public function enemyDie(enemyNum:int) {
var pb:PointBurst = new PointBurst(gamelevel,"Zetsu Extinct!",enemies[enemyNum].mc.x,enemies[enemyNum].mc.y-20);
gamelevel.removeChild(enemies[enemyNum].mc);
enemies.splice(enemyNum,1);
}
public function heroDie() {
// show dialog box
var dialog:Dialog = new Dialog();
dialog.x = 175;
dialog.y = 100;
addChild(dialog);
if (playerLives == 0) {
gameMode = "gameover";
dialog.message.text = "Game Over!";
} else {
gameMode = "dead";
dialog.message.text = "Yoi Failed...";
playerLives--;
}
hero.mc.gotoAndPlay("die");
}
public function getObject(objectNum:int) {
if (otherObjects[objectNum] is Treasure) {
var pb:PointBurst = new PointBurst(gamelevel,100,otherObjects[objectNum].x,otherObjects[objectNum].y);
gamelevel.removeChild(otherObjects[objectNum]);
otherObjects.splice(objectNum,1);
addScore(100);
} else if (otherObjects[objectNum] is Key) {
pb = new PointBurst(gamelevel,"Got Key!" ,otherObjects[objectNum].x,otherObjects[objectNum].y);
playerObjects.push("Key");
gamelevel.removeChild(otherObjects[objectNum]);
otherObjects.splice(objectNum,1);
} else if (otherObjects[objectNum] is Door) {
if (playerObjects.indexOf("Key") == -1) return;
if (otherObjects[objectNum].currentFrame == 1) {
otherObjects[objectNum].gotoAndPlay("open");
levelComplete();
}
} else if (otherObjects[objectNum] is Chest) {
otherObjects[objectNum].gotoAndStop("open");
gameComplete();
}
}
public function addScore(numPoints:int) {
gameScore += numPoints;
scoreDisplay.text = String(gameScore);
}
// update player lives
public function showLives() {
livesDisplay.text = String(playerLives);
}
// level over, bring up dialog
public function levelComplete() {
gameMode = "done";
var dialog:Dialog = new Dialog();
dialog.x = 175;
dialog.y = 100;
addChild(dialog);
dialog.message.text = "Level Complete!";
}
public function gameComplete() {
gameMode = "gameover";
var dialog:Dialog = new Dialog();
dialog.x = 175;
dialog.y = 100;
addChild(dialog);
dialog.message.text = "You Got the Treasure!";
}
public function clickDialogButton(event:MouseEvent) {
removeChild(MovieClip(event.currentTarget.parent));
// new life, restart, or go to next level
if (gameMode == "dead") {
// reset hero
showLives();
hero.mc.x = hero.startx;
hero.mc.y = hero.starty;
gameMode = "play";
} else if (gameMode == "gameover") {
cleanUp();
gotoAndStop("start");
} else if (gameMode == "done") {
cleanUp();
nextFrame();
}
// give stage back the keyboard focus
stage.focus = stage;
}
// clean up game
public function cleanUp() {
removeChild(gamelevel);
this.removeEventListener(Event.ENTER_FRAME,gameLoop);
stage.removeEventListener(KeyboardEvent.KEY_DOWN,keyDownFunction);
stage.removeEventListener(KeyboardEvent.KEY_UP,keyUpFunction);
}
}
}
here are the list of errors in the coding:
*Line 103, Column 4 1120: Access of undefined property otherObject.
*Line 109, Column 6 1120: Access of undefined property floorObject.
*Line 110, Column 6 1120: Access of undefined property floorObject.
*Line 111, Column 6 1120: Access of undefined property floorObject.
*Line 112, Column 6 1120: Access of undefined property floorObject.
*Line 113, Column 6 1120: Access of undefined property floorObject.
*Line 114, Column 24 1120: Access of undefined property floorObject.
*Line 124, Column 8 1120: Access of undefined property gamemode.
can please someone help me with this i have been scratching my head over it forever
thanks
Your code is full of typos.ActionScript is case-sensitive, and variables need to be spelled correctly!
Line 103:Change otherObject=new Array(); to otherObjects=new Array();
Line 108:Change var floorObjects:Object=new Object(); to var floorObject:Object=new Object();
Line 124:Change if (gamemode != "play") to if (gameMode != "play")