Saturday, June 24, 2017

3D Breakout in ~120 lines

Controls are WASD; game appears after the jump!

Tuesday, June 20, 2017

Choosing hobby projects

I recently read a Derek Yu article, Finishing a Game (Derek designed Spelunky), and one piece of advice has been surprisingly applicable to how I think about hobby projects:
I’ve found that there are three types of games that pique my interest: games I want to make, games I want to have made, and games I’m good at making.
Games I want to make are games where the process itself seems really fun. Maybe the mechanic seems really fun to experiment with, or maybe there’s a character I really want to animate.
Games I want to have made are games where I’m more interested in the result than in getting there. Maybe it’s a “no-limits” concept (“OMG, GTA meets Final Fantasy meets Starcraft meets…”) or just a neat idea that’s not necessarily any fun to implement.
Games I’m good at making are games that are suited to my personality and which I have experience in making. Perhaps there’s a certain genre that you naturally gravitate towards and which you understand the rhythm and flow of very well.
In my opinion, the ideas with the most potential (to be finished, at least) fall into all three categories and also satisfy the requirement “I have the time and resources to actually make this”.
Aside: I've had an interesting relationship with hobby projects (computer games / programs, board games, math projects, writing projects, etc.) over the years. The earliest attitude I remember was "It's really important for me to finish this project"; I used to feel really bad when I didn't finish projects. Fairly recently, I semi-decided to stop feeling bad about this, which was probably possible because I'd decided that my impact on the world was going to come from my job, and so the relieved pressure on hobby projects made it possible to not worry about whether I finish hobby projects. Sometimes I say "my hobby is starting projects", and I'm a lot more satisfied with this approach.

However, it's still unsettling to me sometimes to unexpectedly realize that I probably won't finish a project. Derek's trifecta let me put labels on common causes:
  1. I no longer want to work on the project: specifically, the project enters a phase that I'm actually not interested in. For example, in board games, that's creating a lot of art or separate components (like cards) and playtesting; in computer games, that's creating art, animation, levels, etc.; in programming, it's most often a stage where I find out that the appropriate tool is a complex library that will feel like working at Google to use (i.e. instead of writing programs from scratch, I spend my time decoding bad documentation). This is the most common reason for me not finishing a project.
  2. I no longer want to have finished this project: my aesthetic for projects fluctuates a lot, so sometimes I wake up and no longer see the appeal of a project. I usually find that if I just want a day or a week, it comes back, but that's not always true. This is the least common reason for me never finishing a project, but is a common delay.
  3. I'm no longer good at this project: I don't actually understand how this is different from 1. I don't really like doing things I'm not good at. Is that typical for humans?
I accidentally started applying Derek's method after reading his article, and it's increasingly affecting how I choose hobby projects; although I'm pretty chill these days about not finishing projects, I do like finishing them if I can, and in a perfect world I finish more of them. In particular, I've started seeking projects that aren't likely to run into stopping-cause 1, e.g. game-programming projects in genres that don't require much content creation. During a project, I can sometimes anticipate that a plan is likely to run into cause 1, and then make an alternate plan that avoids it. I've started to learn 3d graphics, since I think basic 3d graphics (of the kind that can be made without spending a lot of time sculpting and animating models) look better than basic 2d graphics (of the kind that can be made without spending a lot of time drawing and animating sprites).

A bigger move I've been considering is to explicitly give up on finishing board games, and just be pleasantly surprised if I do finish one. It looks to me (and I'd be surprised if pro game designers didn't agree) like basically all of board game design is playtesting, and I'm just not very interested in that. (I might find that it's not so hard to make a fun game AI to play against and playtest that way, but so far I've found it to be pretty hard.)

I'm not sure if I'll give up on finishing board games, since it does feel kind of bad. It'll be interesting to see if giving up on finishing makes me less interested in starting board games, and whether I think that's good or bad.

Overall, a surprisingly useful way of thinking -- I read a fair number of advice-style blog posts, and most of them don't turn into useful tools like this! Nice work, Derek. 

Thursday, June 15, 2017

Sunday, June 11, 2017

Tetris in <100 lines of code

Controls: W, A, S, D

0
<script>
var piece, rows = [], delay = 500, key = 0; 
onkeydown = e => key = e.key;
document.body.onload = () => (
 cvs.style.width = "100px", cvs.style.height = "300px",
 piece = makep(), frame(), setTimeout(step, delay));

// respond to player input, draw the board and piece
function frame() {
 move(({w: p => p.r = (p.r+1)%4, s: p => p.y--,
  a: p => p.x--, d: p => p.x++})[key] || (p => p));
 key = 0;
 cvs.getContext("2d").clearRect(0, 0, cvs.width, cvs.height);
 var px = (x, y) => cvs.getContext("2d").fillRect(x, cvs.height-y-1, 1, 1);
 rows.map((r, y) => Object.keys(r).map(x => px(+x, y)));
 eachblock(piece, px);
 if (delay > 0) delay -= .01;
 requestAnimationFrame(frame);
}

// try to move the piece down, clear rows and/or end the game if can't
function step() {
 setTimeout(step, delay);
 if (move(p => p.y--)) return; 
 eachblock(piece, (x, y) => (rows[y] = rows[y] || {}, rows[y][x] = 1));
 score.innerHTML = parseInt(score.innerHTML) + rows.length - 
  (rows = rows.filter(r => Object.keys(r).length < cvs.width)).length;
 if (rows.length >= cvs.height) score.innerHTML += " -- GAME OVER";
 piece = makep();
}

function move(f) {
 var newp = Object.assign({}, piece);
 f(newp);
 if (legal(newp)) {Object.assign(piece, newp); return true}
 return false;
}

var makep = () => ({y: cvs.height-1, x: cvs.width/2 -1|0, r: 0,
 blocks: ["1111", "11\n 11", " 11\n11", "11\n11",
  "111\n1", "111\n 1", "111\n  1"][Math.random()*7|0]});

function eachblock(p, f) {
 var results = [], [x, y] = [p.x, p.y];
 var r = p.blocks == "11\n11"? 0 : p.r;
 var [nextblock, nextline] = [
  [() => x++, () => {y--; x = p.x}], [() => y--, () => {x--; y = p.y}],
  [() => x--, () => {y++; x = p.x}], [() => y++, () => {x++; y = p.y}]][r];
 p.blocks.split("").map(c => {
  if (c == "\n") {nextline(); return}
  if (c == "1") results.push(f(+x + [0,1,2,0][r], +y + [0,0,-1,-1][r]));
  nextblock()});
 return results;
}

var legal = p => eachblock(p, (x, y) => x >= 0 && x < cvs.width && y >= 0
 && !(rows[y] || {})[x]).filter(x=>x).length == 4;
</script>
<canvas id="cvs" width="10" height = "30"
style = "image-rendering:pixelated; border: 1px solid black;">
</canvas> <div id="score">0</div>

Monday, April 10, 2017

Elder Dragon redesigns

The Elder Dragons are not very good cards. How about a redesign?
  1. They should be mostly aimed at Elder Dragon Highlander, so they should do well with multiplayer / casual.
  2. Elder Dragons should be big and cool.
  3. They should be different from normal creatures in some way.
  4. Elder Dragons are not Planeswalkers.
What I ended up doing:
  • These are all creatures with loyalty counters on them. At any given time, their toughness is equal to the number of loyalty counters on them, and they each ETB with 7 loyalty counters on them. Loyalty abilities work the way they do on Planeswalkers (once per turn at sorcery speed).
  • Flavorfully, Elder Dragons are hunting planeswalkers; they're happy to stick around and help you out if their particular form of mayhem is happening, but they're happy no matter who gets hurt.
  • Each one has abilities that call back to particular cards -- they can cast ancient (and hence more powerful) forms of well-known effects.
  • The most powerful ability can't be triggered right away, but shouldn't be too hard to get. (This seemed important for Commander, where they can be re-cast each turn after you hit 7 mana.)
  • These have terrible names, but part of the exercise is not changing the names. Oh well!
The cards:

Arcades Sabboth: can cast improved versions of Biomass Mutation or Wrath of God. Cares about artifacts, enchantments, or lands being bounced or destroyed.



Chromium: can cast improved Castigate or Time Walk + Ancestral Recall. Cares about spells that cause life-drain mostly; I'm not totally happy with this trigger.



Nicol Bolas: can cast a better Reanimate or a goofy ability that lets you make two players attack each other in multiplayer (for Bolas, his character is so well-defined that I felt like I needed to hit it). Cares about death and discard.



Palladia-Mors: can cast double Lightning Helix or a souped-up Overrun. Cares about big creatures fighting.



Vaevictis Asmadi: can cast double Maelstrom Pulse or Channel Fireball! Cares about players getting hit hard or drained for life.

Tuesday, October 4, 2016

Response post #2

It's been a while, so I wanted to reply to at least one comment:

Post: Observer selection effects from scratch
Comment: here
I’m not sure it makes sense to assume that nothing about your observation X favors any of these observers over any other (out of the 101 possible observers distributed among W1 and W2)...
For instance, let’s say... observation B is externally focused (i.e. that apples appear to fall downward). For observation B, it seems that some relevant factors would be how available you think the information is to make that observation (i.e. are there apple trees near others), how likely it is that other people are paying attention, etc. If it seems reasonable that others could be observers too, it seems like that should tilt you more towards believing that you are in W2. I’m not sure, but it seems plausible to me that you should think it’s more than 100 times more likely that you’re in W2 than W1.
Thanks for this! I'm not sure that I understand it, though. As far as I can tell, you're saying that some facts about observation B and world W2 could tell us about how likely each observer in W2 is to observe B, right? Like if almost all of the observers in W2 are near apple trees and B is that apples fall downward, it's likely that many observers in W2 will observe B, but if all of W2's observers are in the middle of the ocean, probably few of them will observe B. Is that right?

If so, I totally agree, and I think it's saying the same thing as my post. In my post, I'm not talking about all observers in W1 and W2, but only about those that do actually observe X. In your example, that'd mean there is 1 observer in W1 who sees an apple fall, and 100 in W2 who see the apple fall. That would be in part because of how many observers total there are in each world, but also largely because of where those observers are placed, whether they're paying attention, etc, and it'd be consistent with W1 having millions of observers far from apple trees, asleep, etc -- W2 would still be favored, even with millions of sleeping observers.

Let me know if I've made a mistake, but I think we're on the same page?