1D Pac-Man
abagames.github.io
abagames.github.io
function bot() {
/* direction of the enemy: to our right (1) or to our left (-1) */
dir = (enemy.x > player.x) ? 1 : -1;
/* if pac-man... */
if (
/* ...has no powerup or powerup expires in less than 10 "ticks" */ powerTicks < 10 &&
/* ...is headed toward enemy */ player.vx == dir &&
/* ...is too close to enemy */ abs(player.x - enemy.x) < 25 &&
/* and if enemy's state is not "eyes flying back" */ enemy.eyeVx == 0
) {
// "ArrowUp" or any arrow key reverses the direction of pac-man
document.dispatchEvent(new KeyboardEvent('keydown', {code: 'ArrowUp'}));
document.dispatchEvent(new KeyboardEvent('keyup', {code: 'ArrowUp'}));
}
}
setInterval(bot, 100);
The strategy is ultra simple. Every 100 ms it evaluates the situation and chooses to move away from the enemy (ghost) if it's too close to it, and has no powerup (or if the powerup is expiring very soon).The corner cases where pac-man dies is the game difficulty progressively increases (the ghost becomes faster) until you eat it, so sometimes some pellets are left in the middle and pac-man doesn't have enough time to eat them until the ghost reaches it. The ghost will progressively get faster and faster and death is guaranteed. You could improve the code by tempting the ghost to get close to one edge, then cross over to the other edge and quickly eat the middle pellets.
Also, as soon as new pellets are added, one should prioritize eating the middle pellets.
Also, one could add code to detect the powerup pellets, and chose to NOT move away from the ghost if it calculates it can eat the powerup pellet before the ghost reaches pac-man.
The trouble I ran into was after eating the ghost, the spawn point of the new ghost was too close to my current position and simply sped over me.
With this and all the considerations you've noted above, I'd probably throw an ML solution at it - imagine it takes less coding time.
Coding that from nothing (and understanding it) could take days. Weeks to months if you dont know ml stuff.
In this particular case I think basic Q Tabular Learning could play optimally, no neural net required.
I could be wrong though, throw a full conv at it without temporal difference and see what happens! (probably a kinda okayish score)
This random exploration is less efficient than back propagation, but at least you don't have to muck around with temporal differences. (And it would be hard to figure out how to tweak the weights for backpropagation in this multi-step game; without falling back to reinforcement learning.)
The problem seems simple enough that evolution via mutation and artificial selection has a decent chance to get good in a reasonable amount of computing time.
But both approaches would be an interesting exercise in their respective domain!
Come to think of it, it might even be an interesting toy problem for a SAT solver! Overkill in the same way as an ML approach, of course.
Something different, but not for me personally.
function bot() {
/* direction of the enemy: to our right (1) or to our left (-1) */
dir = (enemy.x > player.x) ? 1 : -1;
nearWall = enemy.x < 10 || enemy.x > 90
/* if pac-man... */
if (
(
/* ...has no powerup or powerup expires in less than 10 "ticks" */
powerTicks < 10 &&
/* ...is headed toward enemy */ player.vx == dir &&
/* ...is too close to enemy */ abs(player.x - enemy.x)
/* ...the distance before running away can be lower if we're near an edge */
< (dir == 1 ? player.x / 7 : ((100 - player.x) / 7))+7 &&
/* and if enemy's state is not "eyes flying back" */
enemy.eyeVx == 0)
) {
// "ArrowUp" or any arrow key reverses the direction of pac-man
document.dispatchEvent(new KeyboardEvent('keydown', {code: 'ArrowUp'}));
document.dispatchEvent(new KeyboardEvent('keyup', {code: 'ArrowUp'}));
}
}
setInterval(bot, 10);[1] https://abagames.github.io/crisp-game-lib-11-games/pakupaku/...
In Chrome, right-click on the page -> View Source. Go to the "Sources" tab. When you open the tree on the left you find a file, that was dynamically loaded (so you wouldn't have found it by just looking at the HTML source of the main page), https://abagames.github.io/crisp-game-lib-11-games/pakupaku/... that seems to contain the game's code. And indeed it's less than 200 lines and implements the core game logic.
There is a main function, update(), containing a conditional "if (!ticks)" that seems to do initialization. "ticks" is probably zero at the start of the game. The code initializes multiple variables, including "player" and "enemy":
if (!ticks) {
player = { x: 40, vx: 1 };
enemy = { x: 100, eyeVx: 0 };
multiplier = 0;
addDots();
powerTicks = animTicks = 0;
}
Then with a bunch of console.log() statements added to the update() function (just edit the code and type Ctrl-S to save it, Chrome allows this sort of in-place code editing), I got a sense that player.x and enemy.x was the x position, with the scale going from 0 (left edge) to 100 (right edge). Then with more console.log() statements I found enemy.eyeVx was normally 0 when the enemy is a ghost or 1 or -1 if the ghost has been eaten and its eyes are flying respectively right or left.Some of the code is less readable, for example:
if (enemy.eyeVx === 0 && (c.a || c.b || c.c)) {
if (powerTicks > 0) {
play("powerUp");
addScore(10 * multiplier, enemy.x, 30);
enemy.eyeVx = player.x > 50 ? -1 : 1;
powerTicks = 0;
multiplier++;
} else {
play("explosion");
end();
}
}
I figured by literally typing "play(...)" in the dev console that these functions play a sound. Therefore based on the sound names "powerUp" and "explosion" I realized this code block is called when pac-man collides with a ghost. And either it has a power-up ("powerTicks > 0") or not, in which case it dies.At this point I had the meaning of "player", "enemy" and "powerTicks" and that's all I needed to write the bot.
eval(atob('ZnVuY3Rpb24gdXBkYXRlKCkgewogIGlmICghdGlja3MpIHsKICAgIHBsYXllciA9IHsgeDogNDAsIHZ4OiAxIH07CiAgICBlbmVteSA9IHsgeDogMTAwLCBleWVWeDogMCB9OwogICAgbXVsdGlwbGllciA9IDA7CiAgICBhZGREb3RzKCk7CiAgICBwb3dlclRpY2tzID0gYW5pbVRpY2tzID0gMDsKICB9CiAgYW5pbVRpY2tzICs9IGRpZmZpY3VsdHk7CiAgY29sb3IoImJsYWNrIik7CiAgdGV4dChgeCR7bXVsdGlwbGllcn1gLCAzLCA5KTsKICBpZiAoaW5wdXQuaXNKdXN0UHJlc3NlZCkgewogICAgcGxheWVyLnZ4ICo9IC0xOwogIH0KICBwbGF5ZXIueCArPSBwbGF5ZXIudnggKiAwLjUgKiBkaWZmaWN1bHR5OwogIGlmIChwbGF5ZXIueCA8IC0zKSB7CiAgICBwbGF5ZXIueCA9IDEwMzsKICB9IGVsc2UgaWYgKHBsYXllci54ID4gMTAzKSB7CiAgICBwbGF5ZXIueCA9IC0zOwogIH0KICBjb2xvcigiYmx1ZSIpOwogIHJlY3QoMCwgMjMsIDEwMCwgMSk7CiAgcmVjdCgwLCAyNSwgMTAwLCAxKTsKICByZWN0KDAsIDM0LCAxMDAsIDEpOwogIHJlY3QoMCwgMzYsIDEwMCwgMSk7CiAgY29sb3IoImdyZWVuIik7CiAgY29uc3QgYWkgPSBmbG9vcihhbmltVGlja3MgLyA3KSAlIDQ7CiAgY2hhcihhZGRXaXRoQ2hhckNvZGUoImEiLCBhaSA9PT0gMyA/IDEgOiBhaSksIHBsYXllci54LCAzMCwgewogICAgLy8gQHRzLWlnbm9yZQogICAgbWlycm9yOiB7IHg6IHBsYXllci52eCB9LAogIH0pOwogIHJlbW92ZShkb3RzLCAoZCkgPT4gewogICAgY29sb3IoCiAgICAgIGQuaXNQb3dlciAmJiBmbG9vcihhbmltVGlja3MgLyA3KSAlIDIgPT09IDAgPyAidHJhbnNwYXJlbnQiIDogInllbGxvdyIKICAgICk7CiAgICBjb25zdCBjID0gY2hhcihkLmlzUG93ZXIgPyAiZyIgOiAiZiIsIGQueCwgMzApLmlzQ29sbGlkaW5nLmNoYXI7CiAgICBpZiAoYy5hIHx8IGMuYiB8fCBjLmMpIHsKICAgICAgaWYgKGQuaXNQb3dlcikgewogICAgICAgIHBsYXkoImp1bXAiKTsKICAgICAgICBpZiAoZW5lbXkuZXllVnggPT09IDApIHsKICAgICAgICAgIHBvd2VyVGlja3MgPSAxMjA7CiAgICAgICAgfQogICAgICB9IGVsc2UgewogICAgICAgIHBsYXkoImhpdCIpOwogICAgICB9CiAgICAgIGFkZFNjb3JlKG11bHRpcGxpZXIpOwogICAgICByZXR1cm4gdHJ1ZTsKICAgIH0KICB9KTsKICBjb25zdCBldnggPQogICAgZW5lbXkuZXllVnggIT09IDAKICAgICAgPyBlbmVteS5leWVWeAogICAgICA6IChwbGF5ZXIueCA+IGVuZW15LnggPyAxIDogLTEpICogKHBvd2VyVGlja3MgPiAwID8gLTEgOiAxKTsKICBlbmVteS54ID0gY2xhbXAoCiAgICBlbmVteS54ICsKICAgICAgZXZ4ICoKICAgICAgICAocG93ZXJUaWNrcyA+IDAgPyAwLjI1IDogZW5lbXkuZXllVnggIT09IDAgPyAwLjc1IDogMC41NSkgKgogICAgICAgIGRpZmZpY3VsdHksCiAgICAwLAogICAgMTAwCiAgKTsKICBpZiAoKGVuZW15LmV5ZVZ4IDwgMCAmJiBlbmVteS54IDwgMSkgfHwgKGVuZW15LmV5ZVZ4ID4gMCAmJiBlbmVteS54ID4gOTkpKSB7CiAgICBlbmVteS5leWVWeCA9IDA7CiAgfQogIGNvbG9yKAogICAgcG93ZXJUaWNrcyA+IDAKICAgICAgPyBwb3dlclRpY2tzIDwgMzAgJiYgcG93ZXJUaWNrcyAlIDEwIDwgNQogICAgICAgID8gImJsYWNrIgogICAgICAgIDogImJsdWUiCiAgICAgIDogZW5lbXkuZXllVnggIT09IDAKICAgICAgPyAiYmxhY2siCiAgICAgIDogInJlZCIKICApOwogIGNvbnN0IGMgPSBjaGFyKAogICAgZW5lbXkuZXllVnggIT09IDAgPyAiaCIgOiBhZGRXaXRoQ2hhckNvZGUoImQiLCBmbG9vcihhbmltVGlja3MgLyA3KSAlIDIpLAogICAgZW5lbXkueCwKICAgIDMwLAogICAgewogICAgICAvLyBAdHMtaWdub3JlCiAgICAgIG1pcnJvcjogeyB4OiBldnggfSwKICAgIH0KICApLmlzQ29sbGlkaW5nLmNoYXI7CiAgaWYgKGVuZW15LmV5ZVZ4ID09PSAwICYmIChjLmEgfHwgYy5iIHx8IGMuYykpIHsKICAgIGlmIChwb3dlclRpY2tzID4gMCkgewogICAgICBwbGF5KCJwb3dlclVwIik7CiAgICAgIGFkZFNjb3JlKDEwICogbXVsdGlwbGllciwgZW5lbXkueCwgMzApOwogICAgICBlbmVteS5leWVWeCA9IHBsYXllci54ID4gNTAgPyAtMSA6IDE7CiAgICAgIHBvd2VyVGlja3MgPSAwOwogICAgICBtdWx0aXBsaWVyKys7CiAgICB9IGVsc2UgewogICAgICBwbGF5KCJleHBsb3Npb24iKTsKICAgIH0KICB9CiAgcG93ZXJUaWNrcyAtPSBkaWZmaWN1bHR5OwogIGlmIChkb3RzLmxlbmd0aCA9PT0gMCkgewogICAgcGxheSgiY29pbiIpOwogICAgYWRkRG90cygpOwogIH0KfQ=='))
when tired just call end()probably better to manipulate the score directly but its not fun that way :)
function update() {
if (!ticks) {
player = { x: 40, vx: 1 };
enemy = { x: 100, eyeVx: 0 };
multiplier = 0;
addDots();
powerTicks = animTicks = 0;
}
animTicks += difficulty;
color("black");
text(`x${multiplier}`, 3, 9);
if (input.isJustPressed) {
player.vx *= -1;
}
player.x += player.vx * 0.5 * difficulty;
if (player.x < -3) {
player.x = 103;
} else if (player.x > 103) {
player.x = -3;
}
color("blue");
rect(0, 23, 100, 1);
rect(0, 25, 100, 1);
rect(0, 34, 100, 1);
rect(0, 36, 100, 1);
color("green");
const ai = floor(animTicks / 7) % 4;
char(addWithCharCode("a", ai === 3 ? 1 : ai), player.x, 30, {
// @ts-ignore
mirror: { x: player.vx },
});
remove(dots, (d) => {
color(
d.isPower && floor(animTicks / 7) % 2 === 0 ? "transparent" : "yellow"
);
const c = char(d.isPower ? "g" : "f", d.x, 30).isColliding.char;
if (c.a || c.b || c.c) {
if (d.isPower) {
play("jump");
if (enemy.eyeVx === 0) {
powerTicks = 120;
}
} else {
play("hit");
}
addScore(multiplier);
return true;
}
});
const evx =
enemy.eyeVx !== 0
? enemy.eyeVx
: (player.x > enemy.x ? 1 : -1) * (powerTicks > 0 ? -1 : 1);
enemy.x = clamp(
enemy.x +
evx *
(powerTicks > 0 ? 0.25 : enemy.eyeVx !== 0 ? 0.75 : 0.55) *
difficulty,
0,
100
);
if ((enemy.eyeVx < 0 && enemy.x < 1) || (enemy.eyeVx > 0 && enemy.x > 99)) {
enemy.eyeVx = 0;
}
color(
powerTicks > 0
? powerTicks < 30 && powerTicks % 10 < 5
? "black"
: "blue"
: enemy.eyeVx !== 0
? "black"
: "red"
);
const c = char(
enemy.eyeVx !== 0 ? "h" : addWithCharCode("d", floor(animTicks / 7) % 2),
enemy.x,
30,
{
// @ts-ignore
mirror: { x: evx },
}
).isColliding.char;
if (enemy.eyeVx === 0 && (c.a || c.b || c.c)) {
if (powerTicks > 0) {
play("powerUp");
addScore(10 * multiplier, enemy.x, 30);
enemy.eyeVx = player.x > 50 ? -1 : 1;
powerTicks = 0;
multiplier++;
} else {
play("explosion");
}
}
powerTicks -= difficulty;
if (dots.length === 0) {
play("coin");
addDots();
}
}Given there aren't many sites that say "just open the console and paste this command to win the big prize", I suspect that any console-only API's aren't very powerful if they exist at all.
https://developer.chrome.com/blog/new-in-devtools-120#self-x...
As well as the usual engagement driving "challenges" like "Omg did you know there's no country starting with Z! Bet you can't think of one!" meanwhile comments are filled with "duuuuh Zanzibaaaar!" and post engagement is >>>>>>>>>>>>
(Admittedly I typically browse with JS disabled...)
And it's quite funny that JS was designed after Self/Smalltalk well before anyone cared about interactive tooling, towards which we evolved independently.
Alan Kay truly is a genius.
1) enemy is running away from player && power is about to expire && player is close to enemy.
2) player is getting too close to enemy's respawn point && player is too far from wall opposite to him && player has little or no power.
3) enemy is close to player (<20 units away) && the player and the enemy are facing each other.
A live preview of the algorithm in action is deployed on github[3].
[1]: https://github.com/creme332/pakupaku-ai
[2]: https://github.com/creme332/pakupaku-ai/blob/main/assets/hig...
Per my experience The optimal strategy is to deprioritize eating the ghost. You can get some nice streaks sometimes eating him but it generally doesn’t help.
What you want to do is use the power up to collect the middle dots. The sides you can collect safely on reflexes alone. By about 3000 points it becomes impossible to recover if you don’t pick up the middle and have consumed your power up. Eating the ghost is ok if it happens incidentally.
Because you’re trying to use the power up to collect the middle that often means the ghost will be further from the middle than you when you finish a line. So finish a line, then 180 immediately to collect the middle again. The worst case is you end up with your power up ending just as you’re finishing a line and you are on the side. But up until very high point values I think it’s always safe to grab the power up as it tends to be on the sides.
Edit: 6800
Might play more if other HNers step up their game :)
OP's score is just ludicrous.
I'd say there is even a (very slight) disadvantage to killing the ghost: As long as it's on screen (and preferably slowed down) you know where it is. Once you kill it, you don't know exactly when and where it will reappear.
Interestingly, the points you get for a kill are NOT tied to how many ghosts you've killed before. They only depend on the speed of the ghost, which in turn depends on your overall score.
So I think one addition to the strategy would be to try to keep the ghosts alive as long as practical early on, then start hunting them when you have, say, 1500+ points and kills can actually give a significant bonus.
A couple more notes: I see now that the score multiplier goes up by 1 for every row cleared AND for every ghost eaten. I don’t think this changes much about my recommended strategy except sometimes you can pause or delay to set up a free ghost kill without breaking the originally stated strat and it’s worthwhile to do so.
At very high levels (15000+) I’m finding it necessary to double tap rapidly to induce small pauses to let the ghost catch up. The timing of these pauses is very tough at high speeds. But the gameplay feels almost safer and more formulaic. Not sure if that’s a function of anything changing in particular. It might just be the normal Strat but fast enough that it normalizes. I might try to do this on a keyboard to get them frame perfect pauses going lol
Edit: worth noting my high score went from 7k to 16k in one game without realizing it had been a particularly good game. The scores ramp up very quick late game and it’s not quite as huge of a gap as you might think
The biggest part of my strategy is to do a lot of stutter steps/dash dancing to try and collect the power up as close to the ghost as possible. Doing so is more likely to lead to a sort of "combo" state where your pacman can spawn kill the ghosts without any additional input. Fast reflexes can keep your combo state going if you know when to stutter step and when to turn back around and start comboing again in the opposite direction Getting your game state into such a combo state is even stronger in the late game as your pacman moves so fast it can almost always set up for a lot of safe kills quickly. And additionally, as you pointed out, by getting a ghost kill for every power up you get, you hasten the speed at which you grow your multiplier
Only other observation has been that the common failure mode is screen wrapping while the ghost is fleeing but without enough time to kill him. Don’t do this. Just wait until he is chasing you to screen wrap.
I'd nearly forgotten about his work until I saw this post, and I'm very happy to see that he hasn't stopped with experimental game design after all these years, and I have a huge backlog of games to play[2].
The best part is when I die I feel like I made a dumb mistake, and that if I improved just a few things I could be way better.
Too many games these days give you the allure of depth and complexity by making things difficult in the wrong ways.
I don't feel like it was my mistake when I got killed often, for one thing the ghost is faster than me, sometimes respawns right next to me, ...
I think it picks the farthest edge from where you ate it. Also, it only spawns at the edges, so don't eat the ghost near the edge if you're not sure where it will spawn.
Yes the ghost is faster than you, but it can't wrap around the stage like you can.
I think I've heard Miyamoto talk about that. When the frustration is all with yourself (rather than with the game itself), and you just know that if you try one more time, that'll be the one where you won't make any dumb mistakes. I think it's the cornerstone of every action game that appeals to me.
The one game that never fails to hook me in that way is Quake 3. Every mistake feels like your own, but with a very clear and obvious path for improvement. Every improvement step feels incremental and doable, none of that “noscope 180 from the bushes where i had zero chance to spot the enemy” situations.
Even on kills where it initially felt like some annoying cheap trick, i watch the killcam, and walk away very impressed and eager to try out their approach myself. And it always makes perfect sense. As opposed to a lot of more modern games, where killcam only frustrates me more.
Imo this is the same thing that made the original DOOM a massive sensation, but I feel like Quake 3 would be easier to relate to for a lot of people (on HN or elsewhere). At the time, I was too young and growing up in a wrong part of the world to catch that original DOOM hype. But with Quake 3 (and imo even quake 1 and 2), that phenomenon truly transcended countries and cultures.
Nowadays we have such capable hardware that we can model near photorealistic stuff in real time. Not quite but not far off.
Those manic sounds and graphical effects are what you get when the hardware is very limited and some very talented people decide they want more, a lot more.
See if you can run up a simulator and get something like a Jeff Minter game running on whatever your IT gear is. You may like Attack of the Mutant Camels - the sounds are quite surreal, given the target hardware.
Consider taking a look at RetroPie or something similar.
If you look closely, the chomping visual doesn't -actually- match up with the chomp sound, it's closer to if there was a track of chomping at a certain chomps per minute that gets toggled on/off if pacman is chomping. Same with everything else-is, so you mute/unmute sounds when the event happens, rather than trigger a sound play.
Or so it sounds and seems to my ears and eyes.
In 2021 he used his Crisp Game Lib to create 111 one button games! That's one every ~3 days.
For me, he's the greatest active game designer in the world today.
The Joys of Small Game Development https://news.ycombinator.com/item?id=37799387
Oh, I had completely forgotten about aba games (the work of Kenta Cho) for some years until seeing this HN post! He has been making little mini-games and posting them on his site (both browser-based and windows-based) for more than 20 years now.
Here's his games (I always liked Torus Trooper): https://www.asahi-net.or.jp/~cs8k-cyu/index.html
For example, in Pac Man, the dots chomping sound seems like it’s just a repeating sound that is toggled on and off, vs. actually syncing with the timing of chomping the dots.
Also, if you check out his other web-based games [1], you’ll notice the same thing — the actions (by tapping) cause a sound that is just repeated at a pre-set rhythm that doesn’t sync with when you tap.
It’s subtle, but can actually have an adverse affect on the performance of the game. I often use sound feedback as a way of timing, and when this doesn’t match what I’m doing, it can definitely throw me off my rhythm.
The Timber Test game [2] is a perfect example of this, particularly on the later levels where you need to cut the log into equal fractions, but the sound timing is always the same, making beeps in unequal intervals.
[1] https://www.asahi-net.or.jp/~cs8k-cyu/browser.html
[2] https://abagames.github.io/crisp-game-lib-11-games/?timberte...
https://dev.to/abagames/how-to-realize-various-actions-in-a-...
Source: https://github.com/abagames/crisp-game-lib/blob/master/src/i...
Probably better to completely disable the arrow keys -- or better, limit it to the spacebar -- to reinforce single-button design.
Or, just intuitively support left & right.
The controls really need to be edited, though. Left should be exclusively left, and right exclusively right. Having them both toggle makes input the primary challenge.
Say, a powerup temporarily turns the screen to 3D, with the ghosts only perceiving the 2D slice. Pac-Man might be a cone that can 'shrink' to squeeze around ghosts, can jump through 3rd dimension to 'teleport', can move back and forth between different 'levels' floors in a building, etc.
Each game really needs to be written in isolation in assembly to work round the limitations, e.g. some might need a sprite multiplexor, some might not.
I made a slightly similar (1.5d?) game for the CHIP-8. Pacman can only go left and right, but the ghosts fall from the sky.
I clicked on this link because it says “1D PacMan”
It made me think “how can Pac-Man be 1D?”
Long ago – early 80s – UHF & cable-syndicated TV station WPIX (NY) offered live broadcast call-in videogames controlled by the caller's voice yelling "pixx!". Here's a peek of some of the forms it'd take:
https://www.youtube.com/watch?v=UJN9eM84Rq8
"Behind the scenes, the voice activation was really operators in the control room pushing the buttons." :)
The simple control of this 1D PAKU-PAKU game (or others you're considering) could be wholly voice-operated. (A fun trigger word nodding to an old 'Friends' episode might be: "Pivot!")
With modern voice-recognition/voice-printing, it might even be possible to have a multiplayer game where even though N players are all yelling the same trigger-word, the recognition disambiguates the speaker to control only their sprite. Or, use a larger vocabulary of control-words for more actions. 'Nibbler' with yelled commands "Up!"/"Down!"/"Left!"/"Right!" might be diabolically frustrating.
And those you probably found already:
• https://codeduino.com/projects/games/1d-pong-game/
• https://hackaday.io/project/185011-twang-esp32-led-strip-1d-...
also this one would be great to have it on an led strip:
Ghost spawns to never be on a player. It’s not fair.
The big dots should give X seconds of ghost eating ability, not just make the existing ghosts edible. That way new ghosts that spawn instantly after you eat a big dot wouldn’t hurt you.
1D dungeon crawler hardware game.
I'm not so interested in this game already. But I think this can improve to close perfect, which player can infinitely escape from enemy, by considering "Offense" aspect, like player's invincible mode and enemy's escape mode.
function bot() { dir = enemy.x > player.x ? 1 : -1; const Xp = player.x; const Xe = enemy.x; const pvx = player.vx; const escaping = (Xp < Xe && pvx < 0) || (Xp > Xe && pvx > 0); const escD = Xp < Xe ? Xp : 100 - Xp; const Vp = player.vx * 0.5 * difficulty; const evx = enemy.eyeVx !== 0 ? enemy.eyeVx : (player.x > enemy.x ? 1 : -1) * (powerTicks > 0 ? -1 : 1); const Ve = evx * (powerTicks > 0 ? 0.25 : enemy.eyeVx !== 0 ? 0.75 : 0.55) * difficulty; const VRatio = abs(Ve / Vp) === 0.5 ? 1.1 : abs(Ve / Vp); console.log(VRatio); const minD = (VRatio - 1) * escD + 3 * (VRatio + 1) + difficulty; const D = abs(Xp - Xe); if (!escaping && D <= minD) { document.dispatchEvent(new KeyboardEvent("keydown", { code: "ArrowUp" })); document.dispatchEvent(new KeyboardEvent("keyup", { code: "ArrowUp" })); } } setInterval(bot, 10);
It's so beautiful and inspiring that one lone coder can still make something with so much joy and playability in an era of multimillion dollar games that take the work of hundreds of people for years on end.
This is a tribute to humanity itself, well done.
This pac man game would be awesome to play on an LED strip as well!
An appreciation of 'Alice in Wonderland' quantum decision making might help too.
----
[0] https://www.youtube.com/watch?v=ncfZWqPm7-4
[1] https://pubs.rsc.org/en/content/articlelanding/2019/lc/c9lc0...
Examples:
- up to 21 million Bitcoins limit - Artificial scarcity
- original 140 character limit for tweets, later expanded to 280, then removed for paid users
- up to 500 users per group in WeChat groupchat
- ip· o· gram. ˈlipəˌgram, ˈlī- : a writing composed of words not having a certain letter (as the Odyssey of Tryphiodorus which had no alpha in the first book, no beta in the second, and so on)
- wireframes vs realistic prototypes (low-fidelity vs high-fildelity)
- monochrome UI for smartphones to remove distractions
- feature phones vs smartphones
- 1D Pac-Man
- etc.
Building this game could be a very good programming excercise as well.
Sent from my cheap-arse $99 Android phone.
Also there is a weird latency thing if you hold the direction key down and then switch to the other direction.
https://drive.google.com/file/d/1C35Wc8toNQ9RN6ylOZ_bK6IRUjb...
However, when I loaded it in a new firefox profile it worked fine. A little confused because I tried running it with dev tools loaded and cache disabled and I still had the problem, but I have to think that the problem has something to do with adblock/privacy badger and some stored data. But maybe disable cache doesn't apply to the local storage API.
It made me click on this link because it made me think “how can Pac-Man be 1D?”
When you eat a ghost, the dots under the ghost don't get eaten. So you skip over 1-2 dots at higher levels when eating a ghost. Then you have to go back to snag them.
One suggestion would be to not care what key is pressed, maybe just have any key change direction?
https://abagames.github.io/joys-of-small-game-development-en...
[edit] It seems that Pixi is one rendering method, and the graphics themselves are abstracted via a "terminal" and "view" class, so the engine focuses on positioning art rather than handling sprite draws directly.
It's a nice set of opinionated choices. In particular, implementing collisions for 2D sprites. I built a game engine like that with particles and collisions (and particle collisions!) in AS3 on top of Starling at one point... sadly, I can't make games with it anymore.
Is it possible to get a higher score than 29?
It better be landscape only on mobile. Add a pitfall bomb (what was that game that created a hole for the critters to fall into, and you could walk over them, but the climb out and the hole heals in a short period... ? Let the ghosts to fall in a hole, then you can place more than one ghost on the path, but you need as many "hole bombs" as there are ghosts on screen+1 (or N)... to not make it rage quit-worthy.
If it were Landscape Only, it would create unnecessary friction.
We have invented a new word for you to incorporate into your comments:
"OPTIONS"
Cant turn. only damage the 1D path, to your advantage/detriment.
Your mechanic is Gauntlet. (best video game ever)
Lode Runner: https://www.youtube.com/watch?v=PWwyhymcDxI