Category Archives: ICM

Amok again!

My proposal for my final project is to take “Words Amok!”, the word game I began working on earlier in the semester, and give it better gameplay and something like substance.

I have discovered that I love making games, which I would never in a million years have guessed before beginning ICM. I’m really not a gamer – I think the last actual system I owned was Nintendo… original Nintendo. But there’s something about being able to create interactions that people return to again and again that seems remarkably powerful, and I am loving thinking about what makes a game satisfying to play, or even perhaps edifying.

At any rate, the games I do regularly play (on my phone while in transit) are word games, the absolute dorkiest of all genres. MMORPGs have whole communities; there are brilliant logic puzzles, exciting shoot-’em-ups, old-school arcade games you try over and over to beat, but word games just aren’t cool.

And I know I’m not going to be the one to make the cool word game, but I can at least imbue it with my own aesthetic and my love of words.

So the areas I will be focusing on are:

1 – First and foremost, gameplay. When I initially began working on this game, it was with the assumption that there would be levels, so that’s a definite. From discussions with folks in class and out, I also think there needs to be some way of clearing the rubble besides just making longer words in less time as the distance between the top of the screen and the top of the pile shrinks (which is impossible, of course, and it’s much less fun with words than it is with blocks in Tetris – it feels like taking a pop quiz from a mean teacher).

Other gameplay changes: make the score more of a thing – as it is now, it just disappears when the game ends, with no sense of tracking your accomplishments.

And then I’m still debating over bells and whistles – I made the decision when I began working on it to not rank letters by value, though that’s not at all set in stone. And I’m not sure if I want “special” tiles that change rules/speed/point values, etc. – sometimes they work great, and sometimes they serve as a distraction. My guess is that it would be good to have some with either limited power (just to keep things interesting) or really unexpected changes (like you only get one consonant per drop until you make three words).

2 – Aesthetics. The opening animation needs to be timed much tighter; I want to make more author-face openings/backgrounds. Possibly I will redesign the tiles a little to make them quirkier. I need to add sounds, even though I’m sure most folks will play with the sound off.

3- Expanding beyond the sketch. I have purchased WordsAmok.com, so I will be migrating it there for its final presentation, but in general, I want there to be some functionality outside of the sketch itself. Certainly I want to situate the canvas on the page in a pleasing way, and have the rest of the page flow aesthetically (even if it’s just a background color) with the game. But past that, I would like to make some continuity and community aspects – it would be great for players to be able to track their personal high scores, and also I would want a leaderboard for everyone playing the game. I have no idea how to do this, but I will look into it.

4 – Enrich the word aspects. Since the beginning, I have wanted to somehow tie the words created by finding letters to a sense of the words’ meanings and/or histories. I experimented with trying to use the wikiquote API to display interesting quotes about or including the words the player has just made, but even a short quote seems like an awful lot to read when you’re in the middle of a game. I definitely want to keep a list of recent words, and/or maybe best words, on the screen. Additionally, I think it might be nice, after the game ends, to invite the user to make a poem using only the words he or she has made/found, and then post that to the main site…

5. Clean up the code. Right now the movement gets very slow and rough in places, which I assume means the game is making the computer work harder than it wants to, and which I’m sure is the result of some bonehead programming on my part (e.g., am I creating endless objects and not getting rid of them properly when they’re no longer part of play? What is causing the tiles to fall slower and slower as the pile grows? What does “too many errors” mean in the P5 editor?)

6 – Make it a mobile app. I want to make it possible to play my game while I ride the subway.

I think these goals are all very much within reach; if I have time (or, much more likely, going forward after ICM is finished) I have a couple of sister games I want to create, and go further into using WordsAmok.com as a home for multiple interesting games.

Sketch is here!

If You Have Ghosts, You Have Everything

GhostScreenShot

This week, I attempted to import data from a wide variety of APIs, with no real luck. Since I wasn’t sure whether it was my problem, my computer’s problem, the APIs’ problem or something else entirely (evil spirits?) I eventually gave up and just used a local file.

So I decided to try to create a digital version of one of my favorite road-trip games, “ghost.” The idea is that players take turns adding letters to a piece of a word. Whoever actually completes the word gets a letter (a la “horse” in basketball), G, H, O, S or T, and the first player who has “GHOST” loses. But you can’t just throw out any letter – if your opponent challenges you and you can’t name the word you were building towards, you get a letter.

(Fun nerdy music history fact – the British folk-rock band Fairport Convention named their third album “Unhalfbricking” after a word the lead singer claimed she was trying to make, playing ghost in the tour van.)

At any rate, this version is very simple. Whoever makes a word gets a point. Whoever adds a letter that doesn’t fit into any makeable word gets a point. (And it occurs to me now that I definitely should have refined that a little further – that bluffing is a real part of the gameplay, and there should be a “challenge” button that rewards a successful challenge [opponent gets a point] or punishes an unsuccessful one… if I work back into this, that’s the first thing I’ll fix).

And then I drew some ghosts with the mouse in Photoshop and integrated them into the background, with its Republican-necktie color scheme.

Inspirations for this were other word games, really – there’s a new-ish one I like called “Spelltower” (http://www.spelltower.com/) and then the classics (Boggle, Scrabble, etc.) in both analog and digital forms. I also found a lot of inspiration looking at the Wordnik API, which lists several projects that use their database, from games to word-of-the-day apps to password generators. Actually Wordnik itself is an inspiration.

Game is here.

Code is here.

Ugly, ugly, ugly code…

Fairly straightforward, I thought – make a sketch based on hats. Specifically, you put a hat on a figure, then something happens.

I felt pretty confident I could do it in regular-old p5, no problem, so the shift to doing it with DOM elements would be basically painless.

Nope.

Loading up the background and foreground images was no trouble – everything orderly, hiding, showing, etc. I used a for-loop to create five copies of my hat graphic, again no problems. I made another for-loop to dictate their behavior, and it was there that the difficulty began – I couldn’t make them behave. Weirdly, the program did work when I moved the first hat off the canvas and called an imaginary seventh hat, but try it on the five hats I intended to create and no dice.

It took about an hour of headscratching before I realized that the problem was that I was calling behaviors in “draw.” Oops. It is still somewhat counterintuitive to put anything involving action (specifically the .mousePressed() function) in “setup,” but it does make sense ultimately – this thing is a thing, more than a JavaScript object is a thing, and it doesn’t need to be redrawn 30 times a second in order to work.

But I left all my weird hacks commented in the code as a badge of my learning curve.

Sketch is here.

Code is here.

Words Amok!

WAScreenShot

I love word games, and I’ve wasted a lot of time playing them over the years. This is my attempt to create one that I haven’t seen before, though it shares a lot of traits with many of the others.

The gameplay is simple – letter tiles fall from the top of the screen, and the player has to make words out of them before they hit the bottom.

And it works! Which kind of amazes me. Still definitely unpolished,very clunky in certain areas (especially the buttons and the timing of the animations) but it does basically what I set out to have it do, and I’m pretty happy just with that.

My hope is to keep working on it and really make it fun to play. Part of what frustrates me about other word games is that they treat words as just strings against which to check the validity of an answer. I hope (and this is something I really haven’t scratched yet) to imbue some sense of the words’ poetics, or at least their meanings – give quotes using interesting words, extra points for particularly cool ones… I have lots of ideas, none of which I’m sure of yet.

And I would also love to build more word games and make a site that offers a variety, for solo and/or social play.

For now, though, I think the next steps are to improve the aesthetics, add level characteristics, have people play it and give feedback, and generally just evolve it in as interesting a way as I can manage.

It’s also kind of a dancing hippo, code-wise – it works, but I’m nearly certain there’s a more elegant way to do everything. That’s something I’d also like to clean up.

You can play Words Amok here!

Code is here.

 

Playing with time

When I tried to come up with a program that began in a unique state, changed over time and responded to user input, my first thought was to make a clock that could be manipulated in some way. The idea was to take the actual present time, display it in a really old-school digital format, and then allow the user to speed it up or slow it down according to his or her whims, with some sort of payoff at “midnight.”

The first challenge was figuring out how to process the time. At first I tried to figure out ways to store hours, minutes, seconds and frames as separate pieces of data, but quickly realized that any time in a day could be easily expressed as an integer, from which one could then extrapolate hours, minutes and seconds. Additionally, the program would only import the time from the computer once – any further progress would be made within the program itself.

EarlyClockScreenshot

The next challenge was figuring out the display. I decided to use seven-segment numbers, and looked around at early digital clocks and watches for inspiration. The goal was to create something a little “off” looking. After a bunch of sketching…

NumSketch

I hit upon a plausible design (though I’m not sure it has ever existed in reality) using just two different rather wonky shapes, one vertical and one horizontal, which also carried the added bonus of being very cut-and-pastable.

And then the rest was just play. I wanted to create two states, one where time was fixed and the other where time was slippery, so I created a “value” variable which sent the program into its different phases and changed on a mouse click. I wanted to reinforce the idea of “overdriving” the clock, so dimmed the numbers when they were running close to normal and then brightened them as time distorted. And I created a fourth state where, if the clock crossed into midnight, the elements of the numbers would go flying around the screen.

The code can be found here.

The sketch can be found here.

Enjoy!

Dipping a Toe into Code

“Honor thy error as a hidden intention”

– Brian Eno and Peter Schmidt, The Oblique Strategies 

This is the first real code I have written since I was a high school freshman. I was pretty overwhelmed playing around with P5 in class, but I got home and decided to keep exploring. And suddenly little tiny bits and pieces of half-remembered stuff came floating back – first Booleans (because who can forget a word like “Boolean”?), then “if/then,” then “else if”… and I was off to the races. I wrote a very very silly program and was deeply impressed with what I had discovered I could do.

And then it came time to Do the Actual Work, and I realized I had no idea. It’s one thing to gather up party tricks and various ways of making the screen do what you want it to, but quite another to use those techniques to tell a story, even a very simple one.

I looked through all of the available functions to see which one would give the most flexibility and ease of use, and I hit on the idea that I could use curve() like the pen tool in Photoshop, if only I could plot coordinates. So I opened my sample photo:

Ann1

sized it to the canvas I wanted to use, and began to move the mouse to where I would normally put anchor points in Photoshop, and noted the values for x and y on a piece of paper.

I then entered them into P5 (using curveVertex()), and a picture began to emerge:

curveAttempt

but not a very compelling one. It’s amazing how difficult it is to draw by conjecture and guesswork, using only numbers and no visual interface. Even with a set of likely points, I was getting weird, lumpy, incoherent shapes, and had absolutely no meaningful way of resolving them.

I then had a think and reread the assignment and decided that it might be more edifying to try to use the actual shapes in the program. It was still painfully slow going, but I developed a process where I decided everything (save a couple choice elements) would be simplified and symmetrical, that I would make the left feature first, “perfect” it by modifying axes, curves, etc., then copy it to the right. In looking for a way to rotate shapes I came across the translate() function, which helped immensely as I could put the center of the face at the center of my coordinate system and easily just change negatives to positives to copy elements from left to right.

But it was still mighty slow going, all trial and error and moving things five pixels at a time. It’s also amazing how  resonant the idea of a face is, and the effect of any element being “off.”

But here it is.

My code is below – there are still a couple of abandoned shapes in there, examples of things I tried that didn’t work. And there’s a very inelegant (since it repeats almost the entire code) odd-interval blinking function that I couldn’t resist throwing in.

function setup() {
createCanvas (460, 600);
}

function draw() {
background(150);

if ((second() % 5 <= 0) && (abs(sin(millis()/1000)) <= 1/4)) {
//left hair 1
fill(50);
noStroke();
translate(230, 260);
quad(-40, 200, -80, -60, -127, -60, -120, 200);
//right hair 1
fill(50);
noStroke();
quad(40, 200, 80, -60, 122, -64, 115, 200);
// face
fill(220);
stroke(0);
strokeWeight(3);
ellipse(0, 0, 214, 260);
//left temple
fill(0);
noStroke();
translate(0, 5);
quad(-115, -45, -70, -45, -70, -5, -115, -35);
//right temple
fill(0);
noStroke();
quad(115, -45, 70, -45, 70, -5, 115, -35);
//nose bridge
noFill();
stroke(0);
strokeWeight(5);
arc(0, -13, 65, 57, PI+QUARTER_PI, -QUARTER_PI);
//bottom of left lens
fill(240);
stroke(0);
strokeWeight(3);
arc(-55, -59, 90, 135, QUARTER_PI / 2, PI-(QUARTER_PI/2), OPEN);
//top of left lens
fill(240);
stroke(0);
strokeWeight(3);
arc(-55, -23, 100, 60, PI+QUARTER_PI / 2, 2 * PI-(QUARTER_PI/2), OPEN);
//bottom of right lens
fill(240);
stroke(0);
strokeWeight(3);
arc(55, -59, 90, 135, QUARTER_PI / 2, PI-(QUARTER_PI/2), OPEN);
//top of right lens
fill(240);
stroke(0);
strokeWeight(3);
arc(55, -23, 100, 60, PI+QUARTER_PI / 2, 2 * PI-(QUARTER_PI/2), OPEN);

//bottom of left eye
noFill();
stroke(0);
strokeWeight(1);
arc(-55, -38, 60, 47, QUARTER_PI / 2, PI-(QUARTER_PI/2), OPEN);
//bottom of right eye
noFill();
stroke(0);
strokeWeight(1);
arc(55, -38, 60, 47, QUARTER_PI / 2, PI-(QUARTER_PI/2), OPEN);

//bridge of nose
noFill();
stroke(160);
translate(0, 2);
strokeWeight(2);
arc(57, -27, 100, 130, HALF_PI+QUARTER_PI+QUARTER_PI/6, PI);
//left nostril
//fill(220);
//stroke(120);
//strokeWeight(2);
//arc(-20, 22, 30, 30, HALF_PI, PI, OPEN);
//right nostril
//fill(220);
//stroke(120);
//strokeWeight(2);
//arc(20, 22, 30, 30, 0, PI-HALF_PI, OPEN);
//center of nose
fill(220);
stroke(120);
strokeWeight(2);
arc(0, 18, 50, 50, QUARTER_PI/2, PI-QUARTER_PI/2, OPEN);
//mouth
noFill();
stroke(100);
strokeWeight(2);
arc(0, 15, 170, 110, QUARTER_PI, PI-QUARTER_PI, OPEN);
//lower lip
noFill;
stroke(160);
strokeWeight(1);
arc(0, 30, 170, 100, HALF_PI-QUARTER_PI/3, HALF_PI+QUARTER_PI/3, OPEN);
//left smile
noFill;
stroke(180);
strokeWeight(1);
arc(0, 55, 110, 50, PI-QUARTER_PI/3, PI, OPEN);
//right smile
noFill;
stroke(180);
strokeWeight(1);
arc(0, 55, 110, 50, 0, QUARTER_PI/3, OPEN);
//left hair
fill(50);
noStroke();
translate(-52, -108);
rotate(PI/3.35);
ellipse(0, 0, 70, 180);
translate(72, -97);
//right hair
fill(50);
noStroke();
rotate(-2*PI/3.5);
ellipse(0, 0, 50, 130);
}

else {
//left hair 1
fill(50);
noStroke();
translate(230, 260);
quad(-40, 200, -80, -60, -127, -60, -120, 200)
//right hair 1
fill(50);
noStroke();
quad(40, 200, 80, -60, 122, -64, 115, 200)
// face
fill(220);
stroke(0);
strokeWeight(3);
ellipse(0, 0, 214, 260);
//left temple
fill(0);
noStroke();
translate(0, 5);
quad(-115, -45, -70, -45, -70, -5, -115, -35);
//right temple
fill(0);
noStroke();
quad(115, -45, 70, -45, 70, -5, 115, -35);
//nose bridge
noFill();
stroke(0);
strokeWeight(5);
arc(0, -13, 65, 57, PI+QUARTER_PI, -QUARTER_PI);
//bottom of left lens
fill(240);
stroke(0);
strokeWeight(3);
arc(-55, -59, 90, 135, QUARTER_PI / 2, PI-(QUARTER_PI/2), OPEN);
//top of left lens
fill(240);
stroke(0);
strokeWeight(3);
arc(-55, -23, 100, 60, PI+QUARTER_PI / 2, 2 * PI-(QUARTER_PI/2), OPEN);
//bottom of right lens
fill(240);
stroke(0);
strokeWeight(3);
arc(55, -59, 90, 135, QUARTER_PI / 2, PI-(QUARTER_PI/2), OPEN);
//top of right lens
fill(240);
stroke(0);
strokeWeight(3);
arc(55, -23, 100, 60, PI+QUARTER_PI / 2, 2 * PI-(QUARTER_PI/2), OPEN);
//top of left eye
fill(255);
stroke(0);
strokeWeight(1);
arc(-55, -15, 60, 47, PI+QUARTER_PI / 2, 2 * PI-(QUARTER_PI/2), OPEN);
//bottom of left eye
fill(255);
stroke(0);
strokeWeight(1);
arc(-55, -38, 60, 47, QUARTER_PI / 2, PI-(QUARTER_PI/2), OPEN);
//center of left eye
fill(0);
noStroke();
ellipse(-55, -26, 20, 20);
//top of right eye
fill(255);
stroke(0);
strokeWeight(1);
arc(55, -15, 60, 47, PI+QUARTER_PI / 2, 2 * PI-(QUARTER_PI/2), OPEN);
//bottom of right eye
fill(255);
stroke(0);
strokeWeight(1);
arc(55, -38, 60, 47, QUARTER_PI / 2, PI-(QUARTER_PI/2), OPEN);
//center of right eye
fill(0);
noStroke();
ellipse(55, -26, 20, 20);
//bridge of nose
noFill();
stroke(160);
translate(0, 2);
strokeWeight(2);
arc(57, -27, 100, 130, HALF_PI+QUARTER_PI+QUARTER_PI/6, PI);
//left nostril
//fill(220);
//stroke(120);
//strokeWeight(2);
//arc(-20, 22, 30, 30, HALF_PI, PI, OPEN);
//right nostril
//fill(220);
//stroke(120);
//strokeWeight(2);
//arc(20, 22, 30, 30, 0, PI-HALF_PI, OPEN);
//center of nose
fill(220);
stroke(120);
strokeWeight(2);
arc(0, 18, 50, 50, QUARTER_PI/2, PI-QUARTER_PI/2, OPEN);
//mouth
noFill();
stroke(100);
strokeWeight(2);
arc(0, 15, 170, 110, QUARTER_PI, PI-QUARTER_PI, OPEN);
//lower lip
noFill;
stroke(160);
strokeWeight(1);
arc(0, 30, 170, 100, HALF_PI-QUARTER_PI/3, HALF_PI+QUARTER_PI/3, OPEN);
//left smile
noFill;
stroke(180);
strokeWeight(1);
arc(0, 55, 110, 50, PI-QUARTER_PI/3, PI, OPEN);
//right smile
noFill;
stroke(180);
strokeWeight(1);
arc(0, 55, 110, 50, 0, QUARTER_PI/3, OPEN);
//left hair
fill(50);
noStroke();
translate(-52, -108);
rotate(PI/3.35);
ellipse(0, 0, 70, 180);
translate(72, -97);
//right hair
fill(50);
noStroke();
rotate(-2*PI/3.5);
ellipse(0, 0, 50, 130);
}