From 7d63ddcb4855daf1aaffbcc00882be3428fdee11 Mon Sep 17 00:00:00 2001 From: shoofle Date: Sat, 20 Jul 2013 14:24:48 -0400 Subject: [PATCH] Blueprints. Completely restructured articles section. Y'know, nothin' big. Yawn. --- articles/__init__.py | 27 ++ articles/article.template.html | 16 + articles/auriga.article.html | 293 +++++++++++++++++ articles/auriga.template.html | 9 + {pages => articles}/city_on_the_river.html | 0 articles/distributed_speakers.article.html | 24 ++ articles/distributed_speakers.template.html | 9 + .../easy_peasy_state_machinesy.article.html | 36 +++ articles/game_log.article.html | 294 +++++++++++++++++ articles/game_log.template.html | 9 + articles/language_for_games.template.html | 15 + articles/mindjail_engine.article.html | 30 ++ articles/mindjail_engine.template.html | 9 + {pages => articles}/miscellany.html | 4 +- articles/oauth.article.html | 32 ++ articles/oauth.template.html | 9 + articles/play_for_x.article.html | 21 ++ articles/play_for_x.template.html | 9 + articles/shoof_shoof_revolution.article.html | 4 + articles/shoof_shoof_revolution.template.html | 9 + articles/snorlax_evaluation.article.html | 64 ++++ articles/spinning.article.html | 49 +++ articles/spinning.template.html | 9 + articles/tamari.article.html | 66 ++++ articles/tamari.template.html | 9 + articles/time_integration.article.html | 35 ++ pages/auriga.html | 305 ------------------ pages/language_for_games.html | 150 --------- pages/tamari.html | 87 ----- project_list.html | 14 +- pure_flask.py | 35 +- pure_flask.pyc | Bin 2009 -> 1512 bytes static/auriga.html | 305 ++++++++++++++++++ {pages => static/backup}/game_log.html | 6 +- static/backup/language_for_games.html | 150 +++++++++ {pages => static/backup}/mindjail_engine.html | 4 +- {pages => static/backup}/oauth.html | 4 +- {pages => static/backup}/play_for_x.html | 4 +- .../backup}/shoof_shoof_revolution.html | 4 +- {pages => static/backup}/spinning.html | 5 +- static/backup/tamari.html | 78 +++++ {pages => static}/distributed_speakers.html | 4 +- static/favicon.png | Bin 0 -> 765 bytes static/shoofle.css | 50 +-- static/tamari.dot | 23 -- 45 files changed, 1687 insertions(+), 632 deletions(-) create mode 100644 articles/__init__.py create mode 100644 articles/article.template.html create mode 100644 articles/auriga.article.html create mode 100644 articles/auriga.template.html rename {pages => articles}/city_on_the_river.html (100%) create mode 100644 articles/distributed_speakers.article.html create mode 100644 articles/distributed_speakers.template.html create mode 100644 articles/easy_peasy_state_machinesy.article.html create mode 100644 articles/game_log.article.html create mode 100644 articles/game_log.template.html create mode 100644 articles/language_for_games.template.html create mode 100644 articles/mindjail_engine.article.html create mode 100644 articles/mindjail_engine.template.html rename {pages => articles}/miscellany.html (98%) create mode 100644 articles/oauth.article.html create mode 100644 articles/oauth.template.html create mode 100644 articles/play_for_x.article.html create mode 100644 articles/play_for_x.template.html create mode 100644 articles/shoof_shoof_revolution.article.html create mode 100644 articles/shoof_shoof_revolution.template.html create mode 100644 articles/snorlax_evaluation.article.html create mode 100644 articles/spinning.article.html create mode 100644 articles/spinning.template.html create mode 100644 articles/tamari.article.html create mode 100644 articles/tamari.template.html create mode 100644 articles/time_integration.article.html delete mode 100644 pages/auriga.html delete mode 100644 pages/language_for_games.html delete mode 100644 pages/tamari.html create mode 100644 static/auriga.html rename {pages => static/backup}/game_log.html (97%) create mode 100644 static/backup/language_for_games.html rename {pages => static/backup}/mindjail_engine.html (98%) rename {pages => static/backup}/oauth.html (97%) rename {pages => static/backup}/play_for_x.html (96%) rename {pages => static/backup}/shoof_shoof_revolution.html (87%) rename {pages => static/backup}/spinning.html (95%) create mode 100644 static/backup/tamari.html rename {pages => static}/distributed_speakers.html (92%) create mode 100644 static/favicon.png delete mode 100644 static/tamari.dot diff --git a/articles/__init__.py b/articles/__init__.py new file mode 100644 index 0000000..852fbec --- /dev/null +++ b/articles/__init__.py @@ -0,0 +1,27 @@ +import os +from flask import Blueprint, render_template, abort +from jinja2.exceptions import TemplatesNotFound + +folder = "articles" +bloop = Blueprint("articles", __name__, template_folder="") + +@bloop.route("/") +def main_page(): + return render_template("project_list.html") + +@bloop.route("//") +def render_article(page_name): + # Arguably, the various options for how to render (templates, articles, flat html) could be stuck into various subdirectories. + # Ultimately I don't want to do this because it's supposed to be lightweight - I should be able to chuck this __init__.py file into + # any folder and start generating stuff correctly. But whatever! + + # Let's find some test filenames! + file_name = os.path.join(folder, page_name.replace("-", "_")) + + # Is there a template by that name? This list is in the priority order for rendering. + extensions = [".template.html", ".article.html", ".html", ""] + + try: + return render_template([ file_name + suffix for suffix in extensions ]) + except TemplatesNotFound as e: + abort(404) \ No newline at end of file diff --git a/articles/article.template.html b/articles/article.template.html new file mode 100644 index 0000000..4f00453 --- /dev/null +++ b/articles/article.template.html @@ -0,0 +1,16 @@ + + + + {% block head %} + + How should this website be laid out? + + + + {% endblock %} + + + {% block body -%} + {%- endblock %} + + diff --git a/articles/auriga.article.html b/articles/auriga.article.html new file mode 100644 index 0000000..5483b59 --- /dev/null +++ b/articles/auriga.article.html @@ -0,0 +1,293 @@ + +
+

Shit, I better make this server work.

+

On the layout of folders and files!

+

Basically I'm not sure how I should lay out this website. The current organization looks something like this:

+
    +
  • server/ (server root) +
      +
    • main_page.html
    • +
    • pages/ (articles with nice links) +
        +
      • auriga.html (accessed as http://example.com/auriga/)
      • +
      • language_for_games.html (http://example.com/language-for-games/)
      • +
      • tamari.html (http://example.com/tamari/) +
      +
    • +
    • static/ +
        +
      • stylesheet.css (global stying)
      • +
      • favicon.ico
      • +
      • banner.png
      • +
      • tamari/ (files for an article) +
          +
        • figure_1.png
        • +
        • figure_2.png
        • +
        • experiment_data.csv
        • +
        +
      • +
      +
    • +
    +
  • +
+

This has a number of problems. First and foremost, barely any thought has gone into naming of folders and files - which is because not much thought has gone into their placement and hierarchy.

+

The server is written using Flask, and this is the rundown on how it works when a user goes to http://example.com/some-path/:

+
    +
  1. If the path requested is the root of the website, render main_page.html.
  2. +
  3. If the path requested begins with static/, render a file from the static content directory.
  4. +
  5. Else, convert the path (some-path) into the filename (pages/some_path.html) assuming it's an article. If it exists, render it.
  6. +
  7. If there's no article by that name, display the miscellaneous articles page. Or 404. The current behavior isn't very high on my priorities.
  8. +
+

There are a bunch of problems with this system - it's kind of brittle, file paths have the meaningless-to-the-user static/ at the beginning, and it means that when I include a resource in http://example.com/tamari/ (the file pages/tamari.html) I have to use a path like ../static/tamari/figure_1.png - when I should be able to reference files related to the Tamari article by a closely-related URL: ./figure_1.png.

+

All this points to the idea of having my articles be in folders, so it would look something like this:

+
    +
  • server/ (server root) +
      +
    • main_page.html
    • +
    • articles/ (articles with nice links) +
        +
      • auriga/ +
          +
        • auriga.html
        • +
        +
      • +
      • language-for-games/ +
          +
        • language_for_games.html
        • +
        +
      • +
      • tamari/ +
          +
        • tamari.html
        • +
        • figure_1.png
        • +
        • figure_2.png
        • +
        • experiment_data.csv
        • +
        +
      • +
      +
    • +
    • static/ +
        +
      • stylesheet.css (global stying)
      • +
      • favicon.ico
      • +
      • banner.png
      • +
      +
    • +
    +
  • +
+

And hell, at this point having the filenames match the folders (auriga/auriga.html) is just a pain, so we could name the main page in each folder index.html, and suddenly this looks very standard. So why don't I do this?

+

Well... I don't know. But something always bothered me about the index.html standard. Maybe it's just that my article about Tamari lattices isn't an index, so I don't think it should be called one. That's certainly part of it. Also, some of my articles and pages are going to be very short - in theory I'd like them to be able to be tiny snippets, which could be included on other pages. It'd be annoying to have a subfolder for every single article I write! But... maybe not too annoying. I'll have to think about that.

+
+

But what are we doing?

+

What would my ideal web framework be? Well, something like Flask, really. I like flask because this is easy:

+
+@app.route("/some-convoluted/url/<options>/with-stuff/")
+def render_shoofle_page(options=None):
+if options == "bananas":
+return render_page("bananas.html")
+else:
+return render_page("some_other_template.html", options=options)
+

And, lo and behold, I can go to example.com/some-convoluted/url/bananas/with-stuff/ and see the right generated page! I like having that capacity at my fingertips. In particular, it makes it easy to respond to various other kinds of requests. That's what I really like about Flask - I feel like I can do everything a webserver can do, but it all takes the same very small amount of work. This compares to, say, PHP, where it's easy to make pages... But more complicated behavior, especially anything involving dynamic URL paths, involves jumping through hoops.It might seem like an overblown complaint, but the integrity of my mental models is important to me - and in PHP, I don't understand what gets sent to the requestor. If I make a Flask route returning JSON, I know it's exactly what a script wants. With PHP, I would hope that it wouldn't have weird headers or content type or additional markup. Plus, you have to work with PHP, which I would wish on no one.

+

I don't like Flask because I need to specify all the behaviors, and I'm worried that I'll get it wrong (thus, this article).

+

My ideal world solution would be something that lets me pass requests for subpaths to other python modules:

+

    +
  • server/ +
      +
    • server.py
    • +
    • main_page.html
    • +
    • articles/ +
        +
      • articles.py
      • +
      • auriga.html
      • +
      • language-for-games.html
      • +
      • tamari.html
      • +
      +
    • +
    • games/ +
        +
      • games.py
      • +
      • scores.csv
      • +
      • play_the_game.html
      • +
      • game.js
      • +
      +
    • +
    +
  • +
+

Aaaand how it works would be simple:

+
    +
  1. A request comes in to http://example.com/tamari/, and it is handled by server.py.
  2. +
  3. server.py says "Okay, I don't have any specific handlers that do anything with this URL, but I think articles/articles.py does!" and sends the request to articles.py.
  4. +
  5. articles.py receives a request for /tamari/ and has a rule for handling that, and so it renders the appropriate HTML file.
  6. +
+

The basic idea is that requests can be routed to other python modules. If those modules can be reloaded easily (or automatically!), this would mean I could write complicated URL handling in subdirectories of my website - without having to touch any of the code that handles anything else. It means I can modularize - and that's awesome.

+
    +
  1. An HTTP GET request comes in to http://example.com/games/points, and server.py takes it.
  2. +
  3. server.py doesn't know what to do with it, but it knows that requests for URLs starting with /games should be handled by games/games.py.
  4. +
  5. games.py receives an HTTP GET request for /games/points, and so it can respond to this with a JSON object that tells the requestor how many points they got.
  6. +
+

This is the main point of the tiered handlers system - that I can write handlers for all kinds of requests, without having to interact with other parts of my website.

+

Basically, I want to combine the simple directory structure of PHP websites with the principles of Flask I really like - separating content from server logic, keeping things simple, and having control.

+

Here's how I imagine this would work:

+
+from flask import Flask, render_template, redirect
+
+app = Flask(__name__)
+
+def main_page(): return render_template("main.html")
+app.add_url_rule("/", main_page)
+
+import games
+app.add_module_url_rule("/games/<path:rest>", games)
+

As usual, don't think too hard about the syntax I just proposed. The point is to distribute URL routing logic throughout multiple files, so that my entire website isn't controlled by one brutalistic overlord server.py file that decrees the entire path of every request. I want things split up, dammit!

+
+

Now I'm reading up on the Flask docs, and it turns out that it already has the capacity for this. Unfortunately, it's complicated and I'm whiiiny.

+

It seems that Flask provides the developer with three options:

+
    +
  • Package Organization: Essentially, break up your server code, like configuration and URL route assignment, into separate files. This doesn't actually solve any of my issues - most of what this does (as far as I can tell) is separate the server initialization logic from the routing and response logic. Plus, it makes it easier to use your application in something else. More on this soon.
  • +
  • Blueprints: Very close to what I want. I could define a Blueprint object in articles.py. This Blueprint object would register all the routes and endpoints that I want to register - and then, when the server starts, it gets all of those from the blueprint. This means we can actually factor views out. I'm really not sure, though, what this affords that the organizing your application as a package doesn't.
  • +
  • Application Dispatching: The most powerful of the three! This is a slightly lower-level solution, wherein you actually run multiple applications concurrently, and dispatch requests to them. This is kind of overkill, but at least it's nice to know that it's possible - things like the async_app example from previous could really make sense, if the app is large enough in scope. Application dispatching means you can just pull in another application and route things to it on the WSGI layer. Or something.
  • +
+

Okay, but... how do they score up? I have some requirements:

+
+
Organization
+
I want to define the behavior for example.com/articles/tamari/ in the same place as example.com/articles/language-for-games/ is defined, but in a different file from where example.com/games/hunt-the-wumpus is defined.
+
Simplicity
+
Defining a URL route and endpoint for each subfolder of my application shouldn't be much more complicated than defining behaviors in a flat system. Ideally, registering all the behaviors for the articles/ subpath is no more complicated than app.route("/articles/", articles_handler)
+
Ease of Extension
+
I'm writing a family of pages. They're starting to get a little too complicated. It should be really easy to go from that situation to having a nicely-delineated subsection of my website.
+
Do I Need to Restart the Server?
+
I hate restarting the server. Maybe this is a little thing, but gah! I want the server to detect changes automatically! This shouldn't be that hard. Of course, this is a much thornier topic when it comes to subpages that might store their own state... So it might be out the window unless I want to get my hands way more dirty.
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

How do they fare up?

OrganizationSimplicityEase of ExtensionNo Restart Required?
Current
Organize as a Package
Blueprints
Application Dispatch
+
+

In light of this chart, it seems like Blueprints are the clear option. Also, "organize as package" is really vague. What they suggested didn't sound very helpful - but making python packages (finally learning how to use __init__.py files!) will probably get used in the solution I'm thinking I'll use, which looks something like this:

+
    +
  • server/ +
      +
    • server.py
    • +
    • articles/ +
        +
      • article_routing_blueprint.py
      • +
      • main_page.html
      • +
      • auriga.html
      • +
      • language-for-games.html
      • +
      • tamari.html
      • +
      • tamari_files
      • +
      +
    • +
    • games/ +
        +
      • games_blueprint.py
      • +
      • scores.csv
      • +
      • play_the_game.html
      • +
      • game.js
      • +
      +
    • +
    • static/ +
        +
      • favicon.ico
      • +
      • stylesheet.css
      • +
      • lol_a_cat.jpg
      • +
      +
    • +
    +
  • +
+

If there's static content that is globally important, with simple routing rules, it can - of course - be stored in the server root's static/ directory. Static file handlers don't take much configuration, so this can go in the root.

+

All the files related to articles are contained in the articles/ directory, or subdirectories. article_routing_blueprint.py contains a blueprint definition that tells the server how to route calls that result in the rendering of the various articles I've written.

+

All files related to the little javascript games I've made, on the other hand, go in the games/ directory. State for this application is managed by games_blueprint.py, which is sort of like a very tiny application. I think it's possible to keep track of state, if you remember to keep variables global... This is tricksy, but should be doable - and if it gets larger, it should almost certainly be developed as a separate application entirely. At that point, I should figure out how to include multiple applications in one website!

+

I want to finish this bit by writing down some code for the base server.py logic:

+
+from flask import Flask
+
+# app configuration and initialization
+app = Flask(__name__)
+app.debug = True
+
+# content in articles/
+import articles
+app.register_blueprint(articles.blueprint)
+
+# content in games/
+import games
+app.register_blueprint(games.blueprint, url_prefix="/games")
+
+

Okay, one last thing.

+

This has been a lot about how to get some basic extensible functionality working. But wait, someone cries in the distance! Why are we going to such lengths to do all this in the first place, when you're just serving static HTML files? Isn't this way more work than you need?In case you're reading this article long after I've finished these changes (I hope so, 'cause I should get to work on this immediately), at this point all the articles were static HTML files. That voice speaks the truth.

+

Thanks for asking. The answer is that I want to change how this site is structured, and the articles are the specific thing I want to change. See, as it is, each article has basically the same header at the top, and basically the same footer at the bottom, and... Well, they're just all enclosed in the same stuff, and so when I change the styling of the website - or, horror of horrors, try to add a sidebar across the hwole site - I have to change all of them! And, as I think we've been over before, I'm lazy.

+

Conveniently, as I've been obsessively reading discussions of standards-compliant HTML5 and document formats and such lately, this laziness messes with my indomitable enthusiasm for conceptually pleasing solutions. Here's what I've got:

+
    +
  • Articles on my site are blocks of text, consisting of my rambling on some topic. Something I've written down. They can have some amount of associated content, but for the most part they're just text. So, I'm going to reduce them down to their bare minimum of unique content - in many cases, just an <article> tag containing what I wrote!
  • +
  • But sometimes I want to have multiple articles display in one page! Sometimes my articles are single paragraphs. Sometimes they can be grouped into related, uh, groups! What do we do then? The answer is templates. Flask uses Jinja2, a perfectly nice templating engine - and why not use it?
  • +
  • If I want to display the articles contained in articles/parkour_is_awesome.html, articles/exercise.html, and articles/how_is_body.html), then I just make a new page, a Jinja2 template, which contains this: +
    +{% include "articles/parkour_is_awesome.html" %}
    +{% include "articles/exercise.html" %}
    +{% include "articles/how_is_body.html" %}
    +
  • +
+

And ta-da! We now have a page which has those three articles in a row.

+

This, of course, also means we have a neat format for how to display things in general. Someone requested access to a URL! Find the file it points to. If it's an HTML fragment, then point the general page template to that file, and render. The general page template is just a template that adds the header, footer, sidebar, universal styling, and so on, as required, for universality.

+

So if you go to example.com/tamari/, the server says "Okay, you want to look at the article in the HTML fragment file articles/tamari.html." It renders the general page template around that article, and returns that to the user.

+

Because I like having the option to look at these things many ways, I'm probably also going to allow for a user to do python articles.py --generate [path], which will simply spit out the the rendered results of going to [path]. I guess that might just be indistinguishable from using wget, but hey. It can't hurt to give more functionality!

+

It remains to be seen precisely how many pages on this site will use the general page template. I don't want to rely on it too much, but, man, is there any reason not to? Basically, it means that I don't have to worry about writing the headers of anything on my website. I guess I'll need to be able to throw it off when I'm making subpages that are intended to be stylistically distinct from the rest of my site.

+
\ No newline at end of file diff --git a/articles/auriga.template.html b/articles/auriga.template.html new file mode 100644 index 0000000..03a0b2b --- /dev/null +++ b/articles/auriga.template.html @@ -0,0 +1,9 @@ +{% extends "articles/article.template.html" %} + +{% block title -%} +How should this website be laid out? +{%- endblock %} + +{% block body -%} +{%- include "articles/auriga.article.html" -%} +{%- endblock %} \ No newline at end of file diff --git a/pages/city_on_the_river.html b/articles/city_on_the_river.html similarity index 100% rename from pages/city_on_the_river.html rename to articles/city_on_the_river.html diff --git a/articles/distributed_speakers.article.html b/articles/distributed_speakers.article.html new file mode 100644 index 0000000..791c8ad --- /dev/null +++ b/articles/distributed_speakers.article.html @@ -0,0 +1,24 @@ +
+

Distributed Speakers

+

(project idea)

+
+

So you want to have a rally. You want to hold a sports competition for your large neighborhood. Maybe you want to broadcast announcements across your company picnic. Maybe you want to throw an impromptu dance party!

+

The common line between all of these scenarios is this: you need a sound system, but you do not have one.

+
+

I first conceived of this while thinking about the specific issue of a dance party. Here's my solution:

+
    +
  • Everyone downloads this free app.
  • +
  • Everyone directs the app to the streaming media source of choice - a web radio station or something.
  • +
  • Each phone in the network listens to the audio it hears, and synchronizes what it's playing to the surroundings.
  • +
+

I liked this idea because I have friends I like dancing with, but we rarely have a sound system set up and in place for dancing in public spaces. But anywhere you need to communicate something to a large number of people but you don't have a sound system, this app would be useful.

+

Let's talk about issues.

+

How do you sync up a bunch of audio sources together on the fly, with limited communication between them?

+

That's the biggest problem, but there are smaller issues - well, they're really just the obstacles we'd need to face:

+
    +
  • How do we make this accept a wide variety of stream types - web radio, youtube, soundcloud, etc.?
  • +
  • How do you do ad hoc networking between mobile devices?
  • +
  • How do we tie this in with youtube playlists, which people will frequently want to play music from?
  • +
  • Is this even possible, when considering the speed-of-sound delay? How far apart can sources be, such that they're perceived as one sound rather than two?
  • +
+
\ No newline at end of file diff --git a/articles/distributed_speakers.template.html b/articles/distributed_speakers.template.html new file mode 100644 index 0000000..18ee10a --- /dev/null +++ b/articles/distributed_speakers.template.html @@ -0,0 +1,9 @@ +{% extends "articles/article.template.html" %} + +{% block title -%} +Impromptu Soundsystems would be a good name for a band. +{%- endblock %} + +{% block body -%} +{%- include "articles/distributed_speakers.article.html" -%} +{%- endblock %} \ No newline at end of file diff --git a/articles/easy_peasy_state_machinesy.article.html b/articles/easy_peasy_state_machinesy.article.html new file mode 100644 index 0000000..443788f --- /dev/null +++ b/articles/easy_peasy_state_machinesy.article.html @@ -0,0 +1,36 @@ +
+
+

Easy Peasy State Machinesy

+
+

Why do I always have to actually use boolean flags (manually setting and unsetting them) to track the states of my entities?

+
+player.shielding = new Toggle()
+keyboard.on('w pressed', player.shielding.next)
+

I'm just throwing this syntax together off the top of my head. The syntax isn't important - what's important is that I should be able to describe this and have it happen.

+
+strobelight.color = new Sequence(['red', 'green', 'blue'])
+strobelight.update = function(dt) {
+strobelight.color.next()
+strobelight.draw()
+}
+

Seriously, don't worry about this syntax - it's ugly and I just threw it together. The point is the kinds of things I should be able to do. This is all totally feasible.

+
+goomba.ai = new StateMachine(['sleep', 'suspicious', 'alert'])
+goomba.ai['sleep'].transitions_to['suspicious'].on(goomba.near(player))
+// the state 'sleep' should transition to 'suspicious' on the event goomba.near(player)
+goomba.ai['sleep'].update = goomba.draw_snores
+
+goomba.ai['suspicious'].transitions_to['alert'].on(goomba.sees(player))
+goomba.ai['suspicious'].update = goomba.search_for_player
+
+// shit we forgot to make a state for just patrolling
+goomba.ai.add_state('patrol')
+goomba.ai['patrol'].transitions_to['sleep'].after(5 seconds)
+goomba.ai['patrol'].update = goomba.wander
+
+// oh hey, she should occasionally wake up to patrol!
+goomba.ai['sleep'].transitions_to['patrol'].after(5-10 seconds)
+

So y'know. State machines are powerful. In order to really use them, you need a robust event system, or else they're a huge pain - it's still possible, you just... need to code it to check the conditions on each update loop. That could get expensive!

+

For the example of checking goomba.near(player) - which has an optional argument for the distance - that function checks if there's already a collision detection object attached to goomba for setting off proximity events. If there is, it returns the event handle, which can be used for binding behaviors to the firing of that event. If a proximity collider of the right size doesn't exist, then it creates one, and returns the event handle.

+

+
\ No newline at end of file diff --git a/articles/game_log.article.html b/articles/game_log.article.html new file mode 100644 index 0000000..7ceb53f --- /dev/null +++ b/articles/game_log.article.html @@ -0,0 +1,294 @@ + +
+

Games I've Played

+
+

Mostly Shooters and Stuff

+
    +
  • Mass Effect 3 Multiplayer
  • +
  • Halo: Combat Evolved +
    + Legendary solo +
    +

    I really liked Halo: CE. It was a strong game, and I've had a soft spot for the Halo story - I don't think it's particularly deep or told with finesse, but it's fun!

    +
  • +
  • Halo 2 +
    + Heroic co-op +
    +

    I've been telling myself I'm going to beat Halo 2 on legendary by myself since the day I beat Halo: CE on legendary. I feel the overwhelming urge to be able to say I've beaten the whole trilogy on the hardest difficulty! On the other hand, it's really goddamn hard.

    +
  • +
  • Halo 3 +
    + Legendary co-op + Legendary solo + All Skulls found +
    +
  • +
  • Halo 3: ODST +
    + Heroic co-op +
    +
  • +
  • Halo: Reach +
    + Heroic co-op +
    +
  • +
  • Gears of War +
    + Insane co-op +
    +
  • +
  • Gears of War 2 +
    + Hardcore co-op +
    +
  • +
  • Gears of War 3 +
    + Hardcore co-op +
    +
  • +
  • Half-Life
  • +
  • Half-Life: Blue Shift
  • +
  • Half-Life: Opposing Force
  • +
  • Half-Life 2 (and Episode 1) (and Episode 2)
  • +
  • Portal
  • +
  • Portal 2 (and co-op)
  • +
  • Army of Two
  • +
  • Army of Two: 40th Day
  • +
  • Operation Flashpoint: Dragon Rising +
    + Gave up after like 30 minutes +
    +

    This game was terrible.

    +
  • +
  • Resistance: Fall of Man +
    + Hard? co-op +
    +
  • +
  • Resistance 3 +
    + Hard co-op +
    +

    These games weren't very deep, but they had decent shooting and good weapons. Resistance 2 didn't have co-op, so I didn't play it - I just watched my friend do it.

    +
  • +
  • Killzone 3 +
    + Veteran? co-op +
    +

    This game offered alternate color modes for colorblindness, which is awesome. Shame the dialogue and plot was so literally laughable. +

    +
  • +
  • Resident Evil 5 +
    + Veteran? co-op + Broke one level +
    +

    My pal and I played this over one break, and we couldn't take it seriously. Once we beat it, we spent our hard-earned points on getting infinite ammo for a few weapons - and decided we'd try to unlock infinite ammo for every weapon. We didn't end up doing that, but we did get so practiced at going through one level - which drops the most money for the time you spend in it by far - that we beat the max difficulty time trial by half. The magnum in that game is ridiculously overpowered.

    +
  • +
  • F.3.A.R. +
    + Fearless? co-op +
    +

    As far as I can tell, these people have no idea what the phrase "survival horror" even means.

    +
+
+
+

Not Shooters

+
    +
  • Pokemon: Red +
    + Beat the Elite Four +
    +
  • +
  • Pokemon: Silver
  • +
  • Pokemon: Diamond +
    + Beat the Elite Four +
    +
  • +
  • Prince of Persia: Sands of Time
  • +
  • Prince of Persia (2008)
  • +
  • Super Smash Brothers: Melee +
    + Every SP challenge +
    +
  • +
  • Super Smash Brothers: Brawl +
    + Every achievement + except one sticker +
    +
  • +
  • Mario Kart: Double Dash +
    + Time Trial Staff Ghosts + Every Cup on Mirror +
    +
  • +
  • Mario Kart: Wii +
    + Gold Medals on Everything +
    +
  • +
  • Okami +
    + All brush techniques +
    +
  • +
  • Legend of Zelda: Link's Awakening +

    I think this might be my #1 pick for best Zelda game.

    +
  • +
  • Legend of Zelda: Ocarina of Time
  • +
  • Legend of Zelda: Twilight Princess
  • +
  • Super Mario 64
  • +
  • Super Mario Sunshine
  • +
  • Final Fantasy
  • +
  • Final Fantasy 6 +
    + Halfway +
    +
  • +
  • Final Fantasy 7 +
    + Halfway +
    +
  • +
  • Final Fantasy 8 +
    + Halfway +
    +

    I feel like including Final Fantasy games that I haven't finished is still reasonable because not finishing a Final Fantasy game involves a similar time commitment as finishing many other games.

    +
  • +
  • No More Heroes +
    + Mild + All Beam Katanas +
    +
  • +
  • No More Heroes 2: Desperate Struggle +
    + Mild +
    +
  • +
  • Guitar Hero +
    + Expert +
    +
  • +
  • Rock Band +
    + Expert Guitar + Medium Drums +
    +
  • +
  • Super Metroid
  • +
  • Metroid: Fusion
  • +
  • Metroid: Zero Mission
  • +
  • Metroid Prime
  • +
  • Metroid Prime 3: Corruption
  • +
+
+
+

More Indie

+
    +
  • World of Goo
  • +
  • Cave Story
  • +
  • Gish
  • +
  • Kerbal Space Program +
    + Low Kerbin Orbit + Round-trip Mun Mission + Minmus Mission +
    +
  • +
+
+
+

Extremely Memorable

+
    +
  • Shadow of the Colossus +
    + Hard + Time Trials (Normal) + Climbed the temple +
    +

    I love this game so much I'm not even going to write about it. My thoughts would take up too much space.

    +
  • +
  • Ico +

    Ico was very pretty, and had some of the same quality that Shadow of the Colossus had, but it didn't tie together nearly so well.

    +
  • +
  • Mirror's Edge +
    + Easy + Test of Faith (Pacifist) +
    +

    This game has the best platforming I've had the pleasure of experiencing - and I'm a sucker for good platforming.

    +
  • +
  • El Shaddai +
    + Normal +
    +

    I loved this game from an aesthetic point of view, but I kept waiting for the story to get interesting. I'm still waiting.

    +
  • +
  • Zeno Clash +

    The aesthetic really makes this game stand out.

    +
  • +
  • Iji +
    + Normal + Somewhat Pacifist +
    +

    Iji remains one of my favorite games of all time, for its excellent pseudo-morality system - one of the best I've seen.

    +
  • +
  • Valkyrie Chronicles +
    + Normal + No deaths +
    +

    Valkyrie Chronicles had such a cool gameplay and storytelling style that I have to include it.

    +
  • +
  • No More Heroes +
    + Mild + All Beam Katanas +
    +

    It's hard to describe this game. It was fascinating.

    +
  • +
  • Elite Beat Agents +
    + Sweatin'! + Hard ROCK! +
    +

    Probably my favorite rhythm game.

    +
  • +
+
+
\ No newline at end of file diff --git a/articles/game_log.template.html b/articles/game_log.template.html new file mode 100644 index 0000000..29b1661 --- /dev/null +++ b/articles/game_log.template.html @@ -0,0 +1,9 @@ +{% extends "articles/article.template.html" %} + +{% block title -%} +I occasionally play some videogames. +{%- endblock %} + +{% block body -%} +{%- include "articles/game_log.article.html" -%} +{%- endblock %} \ No newline at end of file diff --git a/articles/language_for_games.template.html b/articles/language_for_games.template.html new file mode 100644 index 0000000..9a12a25 --- /dev/null +++ b/articles/language_for_games.template.html @@ -0,0 +1,15 @@ +{% extends "articles/article.template.html" %} + +{% block title -%} +Features for a game-focused programming language or library +{%- endblock %} + +{% block body -%} +

I like making games but sometimes I spend a lot of time doing stuff that is just annoying. What if a language or environment were designed to make those things easy?

+
+{%- include "articles/snorlax_evaluation.article.html" -%} +
+{%- include "articles/time_integration.article.html" -%} +
+{%- include "articles/easy_peasy_state_machinesy.article.html" -%} +{%- endblock %} \ No newline at end of file diff --git a/articles/mindjail_engine.article.html b/articles/mindjail_engine.article.html new file mode 100644 index 0000000..99706d7 --- /dev/null +++ b/articles/mindjail_engine.article.html @@ -0,0 +1,30 @@ +
+

Mindjail Engine!

+

This is doubtlessly my most ambitious project. It's a little 2D game engine in python that I've been working on for a while now. It's got some basic physics (not accurate, but they feel right, and that's what counts) and it can zip along pretty respectably! I first started this project in my second year of college, but I soon abandoned it because (surprise of surprises!) I had a lot of classes to work on. When I finally picked it back up towards the start of 2013, I had grown a lot as a programmer and a human being, and there were changes to make. I still love this project, but the primary goal I had here was to never set a deadline for myself. The idea was that I should never say when this project will be done - because it's always in progress. I'm never fully committing myself to a flawed approach, and, although there's a bunch of ugly code here, I'm never afraid to go back and change how I did something.

+

Tell me all about collision detection, it is my favorite subject!

+

I was good at my data structures and algorithms class, and they're concepts I'm deeply familiar with. It's interesting, though, that nothing ever gave me such a good sense for how algorithmic complexities compare until I was doing real-time collision detection. It doesn't help that my chosen language was python - a decision that helped many things in this project, but not speed. People will happily claim that python is too slow to ever make a real game with, but it's just not true. Computers are fast. I'm convinced that python is perfectly capable. There've been a lot of data structures I used for this, and the source is somewhere - I'm going to put those online at some point.

+
    +
  • When I started out, of course, I did the brute force N^2 test, because I just wanted to get objects moving around on the screen. This rapidly bottomed out once I got more than probably 30 objects on the screen. I don't remember, it was a long time ago! I knew from the start that I was going to have to do something better, so I did.
  • +
  • The next step was to roll something more advanced of my own. It had problems, but I'm still proud of how well it worked - I called it a sorted search list. The idea was that the list of objects in the world would be simultaneously kept sorted by each object's maximum and minimum extents on the x- or y-axes. When you're searching for a collision, then, you can cut down how many checks you do by searching upwards and downwards through the list until you find an object which is too far away to intersect - at which point no further objects could possibly be touching you. I should make a picture. Anyway, this system worked alright - if I remember correctly, I got up to around 50 objects on screen before it started slowing down a lot. I kept this around as a supporting data structure, but I didn't realize the massive flaws in it - in order to be certain about objects Foo and Bar not colliding, you need to run the check for Foo against Bar as well as Bar against Foo. Anyway.
  • +
  • +

    After that I finally decided to ask the great oracle Google for help. Why didn't I use the knowledge of others before that? Well, they never really covered collision detection-specific data structures in my classes... I found out about a thing called a quadtree, and I was hooked. The idea is to recursively divide space into quadrants - objects northeast of center, northwest of center, and so on. This is the structure I spent the most time on, because I was dead-set on making it work. It was just so cool!

    +

    Well. That was silly of me. There are a lot of problems. Suppose Foo is in the northwest corner, but Bar overlaps between northwest and northeast! There's no simple solution, and mine was to have a secondary data structure, which stored objects which couldn't fit into any of the corners.

    +

    At this point, I realized a structure-agnostic speedup I could use: Be mindful of whether objects move or not. If two objects never move, you never need to see if they collide. With this, the system got up to probably 80 objects on screen, with most of them stationary. Point being, it was a lot faster. I needed more, though! There was a huge problem:

    +

    The quadtree wasn't balancing. Balancing a regular tree data structure is one thing, but quadtrees have two dimensions to balance at once, which makes it infinitely more difficult... And I couldn't find a tutorial on it, nor figure it out on my own. All the tutorials either dealt with worlds that had a size limit for objects, or discrete-position finite-world grid systems. I didn't want to do either of those! So I started panicking.

    +
  • +
  • The next solution was to try variations on quadtrees. I tried a bunch - changing what supporting data structure I used, switching from a strict quadtree - at each level dividing in quadrants - to a k-D tree: At each level, it divides the world in half, and switches the lines along which it divides the world each level. They're a little more flexible in a few ways, and in theory easier to balance (because you don't need to balance two dimensions at the same time so much). Unfortunately, there's a dearth of information about using them for collision detection, because everyone uses them for graphics and not much else. At this point, I was running out of steam for recursive data structures, but I briefly considered extending my k-D tree to be the most general kind of binary space partitioning tree, wherein you don't just cycle your dividing axis through a set of options, but rather you divide along a completely new axis - not necessarily perpendicular! - at each level. This is used for raytracing a lot, but implementing it for these purposes would have been a pain. It would have just meant more balancing and trying to make the trees smart, which I didn't want to get too heavily into. I gave up on this soon after, partly because...
  • +
  • I realized that my secondary data structure didn't work right. You needed to double-check every collision in order to be sure you had gotten all of them, and besides, sorting along one axis had little effect when you were frequently checking collisions which are grouped along the axis it doesn't sort on. I was basically back to square one. That's when I finally gave up on the tree - I would have needed to make it too smart in filling itself, I would have needed to balance it, I would have needed to have a good secondary data structure, and... It wasn't worth it. But then I found the light.
  • +
  • +

    Spatial Hashing was something I'd heard of before, but mostly in the context of grid-based systems, finite worlds, and size-limited systems. I didn't want any of that. The typical spatial hashing scheme goes like this: You have a 2D array of lists. Each list corresponds to one grid space in your world, and so you only need to check against objects in adjacent squares. Here was my problem: Static arrays means you need to have a set size for your world, I don't like strict gridding, and I wanted to account for the fact that I might have large objects! But there's a solution, and, as it always seems to be in the world of data structures for practical everyday purposes, it's hash tables.

    +

    Here's how my system works: The Spatial Hash Grid is a dict. The keys are (x,y) tuples which index into the infinite space of grid squares. The items are a secondary collision-detection data structure, which contain the objects. To find the key for an object, you just have to integer divide its coordinates. Since it just uses python's built-in dicts, it's hashing the tuples - so you never need to pre-specify the size of your world! The thorniest thing to get around was storing large objects. I tried throwing items into all the grid squares they touch, but it was too slow. The solution I ended up using is to make multiple tiers.

    +

    When you put an object in the grid, it checks its size (by looking at the min/max x/y coordinates, which shape objects contain). If it's small enough to fit in this grid - that it will only ever occupy one square and its neighbors - then it goes in the dict we're looking at. Otherwise, the grid makes a new grid - with twice the grid size! Larger objects get shoved up the ladder, and when you're checking for collisions, you traverse up the ladder until you hit the end. This is reasonably fast so long as you don't have unreasonably many objects on different size scales.

    +

    And it works! I haven't done extensive testing, but it goes up to 150 - 40 moving around, and the rest stationary - with 60FPS. I've gotten some slowdown after that (only from adding more moving objects) but it's certainly fast enough for my purposes - and some of the speed loss is from unoptimized rendering - and it should be the case that a sparsely populated world will have nearly no slowdown from additional objects being chucked in.

    +
  • +
+

So I'm using Spatial Hashing, and there're ways for it to be sped up, but collision detection works pretty well for now - certainly well enough to focus on other things.

+

Yes, but what about... anything else?

+

Well, this project is about the engine, so I'm not prepared to go into the actual game I want to make with it, but there's certainly graphics and windowing to talk about. The graphics are, for the moment, all coded up with vertex lists that it generates when it runs, and they're far from optimized or anything. This is good because I learned a lot about OpenGL. This is bad because pretty much all of what I learned is now obsolete, since the computer I've been developing for doesn't... have support... for shaders. Whoops. I want to get more experience with shaders, but I don't have a computer that's good for it right now. Also, that will be more meaningful when I have a better model-<engine pipeline for actually putting content into the engine.

+

Windowing is, for the moment, done through pyglet, which is a lightweight framework for windowing and opengl bindings. I like it okay, but it's not capable of doing much - I'd have to write my own GUI, which would be suboptimal (although not too hard) given that I could just get a framework to do it for me. What I'm trying to do now is switch to using PyQt, having tried wxPython briefly and found it very hard to work with. I'm still working through reimplementing secondary things that pyglet was doing for me, such as keyboard and mouse handling and clock/fps management. That's what the gui branch is for, on github.

+

I guess links would be nice?

+

Oh, yes! Right. The repository is up on github, and it should work for you if you fork it. You'll need to pip install pyglet, or fork their latest dev version if you're on a mac - there's some kind of unresolved problem with OSX or something. That's one of the reasons I want to switch to a different framework - better portability.

+
\ No newline at end of file diff --git a/articles/mindjail_engine.template.html b/articles/mindjail_engine.template.html new file mode 100644 index 0000000..adb5f78 --- /dev/null +++ b/articles/mindjail_engine.template.html @@ -0,0 +1,9 @@ +{% extends "articles/article.template.html" %} + +{% block title -%} +The Mindjail Engine! +{%- endblock %} + +{% block body -%} +{%- include "articles/mindjail_engine.article.html" -%} +{%- endblock %} \ No newline at end of file diff --git a/pages/miscellany.html b/articles/miscellany.html similarity index 98% rename from pages/miscellany.html rename to articles/miscellany.html index b242f53..27e497d 100644 --- a/pages/miscellany.html +++ b/articles/miscellany.html @@ -3,8 +3,8 @@ Projects, by Shoofle - - + +
diff --git a/articles/oauth.article.html b/articles/oauth.article.html new file mode 100644 index 0000000..19db4a0 --- /dev/null +++ b/articles/oauth.article.html @@ -0,0 +1,32 @@ +
+

OAuth Adventures

+

(how does it work?)

+

I recently had some exciting adventures involving trying to make a bot that posted to Tumblr. I eventually got it working (well, so far, just the posting part, but that's the one I didn't understand!)) but decided that I should write down what I learned about OAuth - and specifically, how to use it to write robots that post on their own.

+

The problem is that all the many guides to using OAuth are specifically geared towards the expected use case - I want my app to access users' data on other services like twitter! That's not what I want. I want my bot to be able to periodically run and access some tumblr accounts. This seems like it shouldn't be too hard, but I don't want to code in the password to this account! So what do I do?

+

So let's go through this backwards.

+

It's doable, very doable if you're using a library like oauth2, and extremely doable if you're using tumblr's python api! Either way, you're going to end up making requests to the tumblr web api using open authentication, and you're going to need a token to do it.

+

You need an Access Token.

+

An access token is keyed to your application, and it's been verified by Tumblr that a user has verified that you can use it. Think of it as the key to your user's content, which your user hands over to the application.

+

Of course, in your case, you're not going to care much about the exact mechanics of acquiring the access token, beyond the first time. You use the access token to sign your requests. How to do that is something I'm sure there are other tutorials for - if you're using oauth2, you simply need to specify it as the token for your client object:

+
import oauth2 as oauth
+client = oauth.Client(consumer, access_token)
+

consumer here refers to a key/secret pair, which you're given when you register your application. It lets tumblr know (through the oauth protocol) which application this is.

+

The access_token is also a key/secret pair, but you get it when the user logs in through oauth. The key and the secret are two strings of random-looking stuff, which is cryptographic magic.

+

If you're trying to figure out (as I was) how to get your python script to access user-restricted tumblr data, but you don't want to code the password to your account directly into the script, then the access token is exactly what you're looking for. I stuck it in a file that I don't share with anyone, from which my script imports the keys. Now I could share my script, but no one would have my keys - and even if they did, I could revoke its access to my account at any time.

+

If I'm using oauth2, then it works as above, and you use the client object to make your requests. If you're using tumblrpy, then you simply provide the consumer token's key and secret, as well as the access token's key and secret, to the library when you are initializing your client object. It's very easy!

+

Okay but how do you do it?

+

I can tell you're a smart cookie! That's a very good question. It's easy to use the access token, but how do you actually acquire it? As far as I can tell, the answer is to do the standard three-legged authentication process, but only do it once.

+

I just brought up the interactive python console and started typing in code as though I were trying to code a three-legged authentication workflow (this is the one you hear about when you google for oauth tutorials). Here are the steps of that process, in case you forgot:

+
    +
  • Acquire a consumer key and secret when you register your application.
  • +
  • Initialize an oauth.Consumer object with the consumer key and secret.
  • +
  • Initialize an oauth.Client object with your oauth.Consumer object.
  • +
  • Using that client, make a request to the request token url (for tumblr, it's http://tumblr.com/oauth/request_token). The response will contain a request token - a key and secret pair. This is used to acquire your access token!
  • +
  • Produce a URL to which to direct the user you want to get credentials for. This looks something like this: http://tumblr.com/oauth/authorize?oauth_token=[your request token's key goes here].
  • +
  • When a user (such as yourself!) goes there, they will be prompted for what to do, and then when they grant authorization, they'll be redirected somewhere with a "verifier" in the URL arguments. If you were making a standard three-legged application, this would redirect you back to your website, which would then store and use the verifier. You just need to redirect yourself to somewhere and copy/paste the verifier out of your URL bar. The callback URL is set in the tumblr settings for the application.
  • +
  • Now you can use token.set_verifier([some verifier]) to set your request token as being verified as good.
  • +
  • Your request token has been verified, so now you simply make a request (using that token, so client = oauth.Client(consumer, request_token)) to the access token URL - for example, http://tumblr.com/oauth/access_token. The response to this will finally contain... Drumroll!
  • +
+

The access token!

+

So that's how it's done.

+
\ No newline at end of file diff --git a/articles/oauth.template.html b/articles/oauth.template.html new file mode 100644 index 0000000..c9a785b --- /dev/null +++ b/articles/oauth.template.html @@ -0,0 +1,9 @@ +{% extends "articles/article.template.html" %} + +{% block title -%} +I have used OAuth, and lived to tell the tale. +{%- endblock %} + +{% block body -%} +{%- include "articles/oauth.article.html" -%} +{%- endblock %} \ No newline at end of file diff --git a/articles/play_for_x.article.html b/articles/play_for_x.article.html new file mode 100644 index 0000000..f337efe --- /dev/null +++ b/articles/play_for_x.article.html @@ -0,0 +1,21 @@ +
+

Play for X!

+

This is a running project I'm working on. Check it out on another port on this very server, with the caveat that it's not exactly the belle of the ball at the moment. Lately I've not been focusing on the styling and design so much as I've been focusing on...

+

The functionality! The super short version is this:

+
+

Instead of rolling dice to see if you hit a monster, why not play a minigame instead?

+
+

Tabletop roleplaying games (my experience is mostly with D&D 3.5, but I've also been playing Dungeon World and someday I want to run or be in a The Window game) have the well-agreed on standard that you use dice to generate random numbers to check whether you succeed on chance-based tasks. This introduces instability into the game, which makes things tense and interesting and prevents the game from playing the same way every time!

+

But the only input the player actually has on how well they do, then, is the gameplay choices which apply various bonuses to their rolls. And that's kinda sucky! Because now that we all have computers, why not do something more interesting:

+
+

When your character faces a skill-based task, you have to face one as well.

+
+

So what is Play for X anyway?

+

It's a chatroom, plus a bunch of tiny javascript games. You and your friends all log into a room together, and you start playing your game over the voice chat system of your choice. When you (as the DM) decree that a player needs to make a skill check, you simply choose a minigame or microtask from the list, and drag it onto their name. It immediately pops up on their screen, and when they've completed it, their score is posted into the chatroom, and you can make a decision based on how well they did!

+

It's not finished yet. How the various parts of the system talk to each other has changed a bit as I've worked on this - the chat functionality works through websockets, and the games are slated to communicate their results to the client's browser by postMessage passing. This is partially implemented in...

+

The code! The source is available at the github project play-for-x, and you're entirely welcome to inspect, criticize, suggest, fork, whatever, as you like. I'd be interested in hearing if you do something with it! Some parts are pretty messy, but the games themselves are fun to make.

+

Can I check it out?

+

Sure! If you want to just try the games, you should direct your eyeballs or eyeball-equivalents at this lil' website, which lets you try out the minigames I've made, play with their configurations, and so on. It uses a nifty json editor widget that someone made.

+
+

One of my favorite things about working on this project is that it really strips out all the cruft that usually comes with working on game projects - I don't need to worry about the engine, I don't need to worry about rendering engines, I don't need to make lots of textures. I explicitly set a goal of never writing a large game for this project. With that requirement in place, I'm totally free to experiment a lot, and just throw together little javascript games in a few hours. Indeed, most of the games you'll see here took me half an afternoon to write! Experimenting with different forms of gameplay, and exploring exactly that side of the craft, is one of my greatest interests.

+
\ No newline at end of file diff --git a/articles/play_for_x.template.html b/articles/play_for_x.template.html new file mode 100644 index 0000000..f0a942e --- /dev/null +++ b/articles/play_for_x.template.html @@ -0,0 +1,9 @@ +{% extends "articles/article.template.html" %} + +{% block title -%} +Play For X +{%- endblock %} + +{% block body -%} +{%- include "articles/play_for_x.article.html" -%} +{%- endblock %} \ No newline at end of file diff --git a/articles/shoof_shoof_revolution.article.html b/articles/shoof_shoof_revolution.article.html new file mode 100644 index 0000000..4cbeb05 --- /dev/null +++ b/articles/shoof_shoof_revolution.article.html @@ -0,0 +1,4 @@ +
+

Shoof Shoof Revolution!

+

As part of Play for X, I made a javascript-powered clone of DDR, using SVG. I like working with SVG and javascript - I can directly manipulate drawing rules and maintain an incredibly tight feedback loop as I develop. In this case, though, I was so proud of what I made that I kept trying to make it better. It's far from finished and I've set it aside as I work on other projects, but, in theory, this project was to make a simple javascript clone of DDR - but using the web audio API, I was going to make it so you could load in a file, entirely locally, and it would do automatic beat detection and generate an actual track for your song. Unfortunately, audio processing and beat detection is less than easy and simple when you're using javascript. It's a cool project, and I'm interested in returning to it sometime.

+
\ No newline at end of file diff --git a/articles/shoof_shoof_revolution.template.html b/articles/shoof_shoof_revolution.template.html new file mode 100644 index 0000000..7bf7c69 --- /dev/null +++ b/articles/shoof_shoof_revolution.template.html @@ -0,0 +1,9 @@ +{% extends "articles/article.template.html" %} + +{% block title -%} +Javascript DDR Clone +{%- endblock %} + +{% block body -%} +{%- include "articles/shoof_shoof_revolution.article.html" -%} +{%- endblock %} \ No newline at end of file diff --git a/articles/snorlax_evaluation.article.html b/articles/snorlax_evaluation.article.html new file mode 100644 index 0000000..0698674 --- /dev/null +++ b/articles/snorlax_evaluation.article.html @@ -0,0 +1,64 @@ +
+
+

Snorlax Evaluation

+

(define-by-reference, or: lazy evaluation taken to extremes)

+
+

Okay, so I've got a Bomb entity in my game. It's already got the behavior for flying toward its destination. What if I want to make a SmartBomb that tracks a target even if they move? I might do it something like this:

+
SmartBomb.on_update = function (timestep) {
+// other update behaviors go in this function as well
+this.destination = target.position
+}
+

I have to explicitly tell my engine to execute this every frame. I think in terms of this kind of thing already, because I'm a math and physics person. Why can't I define relationships like this more simply? What if, in fact, this was the default way we referred to values? It's worth noting that what I've thought of here is essentially just functional reactive programming. You can go read wikipedia if you want.

+
SmartBomb.destination = target.position
+

Any time something fetches the value of SmartBomb.destination it'll fetch the value of target.position. Writing it this way, instead of explicitly specifying which variables need to be kept up-to-date with other variables (and when they need to be updated!), means it's easier to give variables interesting dependencies on other values - like this shield which should float a certain distance between the player and the enemy:

+
shield.position = player.position + shield.distance * unit_vector(enemy.position - player.position)
+

If and when you do need to store something's value at a specific time, you can do that if you want - for example, this code when a bomb explodes:

+
explosion_location = current_value(bomb.position)
+// or maybe the syntax is like this:
+explosion_location = bomb.position.current_value
+

So far, everything's time-independent. For example, the shield object's position depends only on the positions of other objects.

+

Why not include time-dependence? A bullet has a position and a velocity, and these are related in a way that doesn't change at any point during gameplay. Why not codify that relationship in a similar way?

+
bullet.position = bullet.position + time_step * bullet.velocity
+// or maybe
+bullet.position = bullet.position + bullet.position.time_since_last_calculation * bullet.velocity
+

Okay, if this seems like impossible magic, I'm sorry. Let's walk through it:

+
    +
  1. The global loop asks for bullet.position in order to know where it should be drawn.
  2. +
  3. bullet.position looks at its cached value and sees that it's out of date.
  4. +
  5. bullet.position subtracts timestamps to find the time step between now and whenever it last got calculated, and remembers this as time_step
  6. +
  7. The time-dependence expression is now evaluated, using the last calculated value for bullet.position, and fetching bullet.velocity as normal.
  8. +
+

Hell, why not remember the entire history of a variable's values?Performance. When I fetch the last calculated value for bullet.position, why not go ahead and make that a graph-crawling reference-calculation just like everything else?Reactive time-dependence is something to think about for an interesting mechanic, if nothing else - keeping track of dependencies on history means you could change a value at any point throughout the game's history and see how it changes things... Just like Achron! And the flaws that make it seem infeasible here wouldn't be a problem in, say, a turn-based time game.

+

Performance is a legitimate worry at that point. If you include the full-on time-dependence, then depending on implementation you might end up following a reference back through every frame the game's been through. This approach would obviously fall apart because every value in your world is going to be at least O(t) to calculate, which sucks - there should be no time-dependent complexity - but there are solutions:

+
    +
  • Careful design can ensure that your objects aren't too densely interlinked.
  • +
  • If you know you can do things more efficiently in some strenuous case, you can always hard-code it to extract current values and explicitly pass them around whenever you want.
  • +
  • Variables should carry metadata about when they last changed, and when their predecessors and descendents in the graph have changed - so that, in calculating the shield's position, you don't re-fetch the enemy.position, if you know it hasn't changed.
  • +
+

Besides which, we have fast computers, and without running into hard algorithmic complexity problems (collision detection, I'm lookin' at you!) it's not that easy to overload a modern gaming machine. And hey, maybe having declarative time-dependent values is a pipe dream - I don't care. The core idea is still awesome.

+

Here's an example (with more attention paid to data types) of how I'd come up with a vector that always points to a set offset from the position of bullet.

+
bullet.position = Wrapper(v(1,2))
+offset = v(3,4)
+endpoint = bullet.position + offset
+print endpoint
+--> WrapperSum(bullet.position, offset)
+print endpoint.current_value
+--> v(4,6)
+bullet.position.current_value = v(-10,0)
+print endpoint.current_value
+--> v(-7,4)
+

Once I've started thinking and talking about this, I start wondering how much we need imperative game loops at all, for anything other than those troublesome time dependencies and feedback loops. I wonder...

+
print bullet.contacts
+--> Wrapper(List<Contacts>)
+contact_acceleration = sum(contact.force_to_resolve for contact in bullet.contacts)
+bullet.acceleration = contact_acceleration
+

Assuming that force due to contacts is the only thing present in the world, then the bullet now feels enough acceleration to resolve all the conflicts. I mean, assuming contacts are simple to work with, and they always are. Right? Right?

+

I'm thinking about this a lot because this kind of referencing is very natural for things like defining interlinks between components in my game engine - I've got conflicting interests:

+
    +
  • I want to keep my vectors and properties lightweight, so that a bullet's position should just be a vector, with which I should be able to do math as freely and easily as integers.
  • +
  • But I also want to ensure that the collision component's position is the same as the bullet's position! I'd like to do collision_comp.position = bullet.position
  • +
  • And then I run into the problem that since I want my vectors as primitive as possible, if I change bullet.position then it's not going to change collision_comp.position because pass-by-value is the dominant paradigm.
  • +
+

The solution I've been working with is wrap together sets of related data (as components). The player object has a PositionComponent and a CollisionComponent, and the CollisionComponent stores a reference to the player's PositionComponent so that it can access it at any time. Actually holding those references constant means that there would be problems if the player.position_component suddenly changed to a different object - but that should never happen. I'm satisfied with this for the reasons I implemented it in the first place, but it doesn't get me the dynamic interlinking I'd like from something more like functional reactive programming.

+

So I don't know. I would love to have the capacity for functional reactive programming in my game engines, at the least! It's so useful for making bulletproof dynamic interactive systems.

+
\ No newline at end of file diff --git a/articles/spinning.article.html b/articles/spinning.article.html new file mode 100644 index 0000000..1b9af35 --- /dev/null +++ b/articles/spinning.article.html @@ -0,0 +1,49 @@ +
+

Spinning!

+

I like to spin. If you youtube "fire spinning", you'll find out what I mean. I haven't yet actually spun fire, but I've been spinning my glowsticks and non-fire props for several years now, and I'm pretty dang good, if I say so myself! I started spinning at the University of Virginia (props to my Brown College spinners, and the short-lived poi club!) but now, living in Somerville, Massachusetts, I spin twice a week at the Medford Spinjam (at Tufts!) and the Boston Spinjam (at MIT). Check out their facebook pages to see when and where they meet, if you want to see me (and a bunch of others!) spinning our hearts out.

+
+

Poi!

+

Poi was the first prop I learned. Once I managed to do a three-beat weave, I zoned out until my partner poked me that they were tired, and I realized that I was completely pooped, as I'd just spun for three hours without even thinking about it. That was in 2011, and I've been spinning poi off-and-on (and other props!) since then.

+

I like poi becasue it's relaxing. I can get into it and just spin for hours, and let the prop guide my movements. Gets boring, though!

+

Here's a list of the tricks I can do. If you don't spin a lot, feel free to ignore this. If you do, I apologize for the ones I have kooky names for.

+
    +
  • In-plane spinning
  • +
  • Turns
  • +
  • Butterfly
  • +
  • Thread the Needle (forward only)
  • +
  • Pac-Man Twist
  • +
  • 3-beat Weave
  • +
  • Weave extensions
  • +
  • Wrap-beat weave
  • +
  • 5-beat Weave (forward only)
  • +
  • Reels
  • +
  • Chasing the Moon, Chasing the Sun, Moon Chasing Sun
  • +
  • Chasing where the Sun Don't Shine (same time only)
  • +
  • Windmills
  • +
  • Air Wraps
  • +
  • All manner of bicep wraps and leg wraps
  • +
  • Spiral wraps
  • +
+

I spin a pair of flowlights with crystal cases. I've got the Sol coloration, and I love them dearly (although they have a tendency to turn off at the wrong times). I'm hoping to get a pair of Spectrum flowlights, or possibly build my own. I also have a pair of tailed poi I need to fix - they're so fun to spin!

+

Someday I'd like to build a pair of accelerometer-powered glowpoi - they could change color depending on the acceleration they're feeling. When you're spinning in a circle, that's directly dependent on speed - it could make for really nifty effects!

+
+
+

Staff!

+

Since the end of 2012 I've also been very into spinning staff. I use a full-length staff that I made myself for super cheap - it's heavy as a car, but it's balanced alright. I spin a mix of traditional and contact staff, because what else would I do?

+

I like spinning staff because it's fun. Poi is relaxing, meditative; staff is, to me, much more energetic. I feel like I'm getting to play with this giant whirling toy, whereas with poi I feel like I'm guided by the inertia of the props.

+

Here's a (partial) list of tricks I can do:

+
    +
  • Rotors
  • +
  • Weaves up to 5 beats (I think)
  • +
  • Half-Steves (right-to-left consistently, shakier on left-to-right)
  • +
  • Halos
  • +
  • Angel Rolls (only right-to-left)
  • +
  • Shoulder-Neck-Shoulder
  • +
  • Neck wraps, shoulder wraps
  • +
  • Fishtails (forward only, right hand only)
  • +
  • Rainbow Stalls
  • +
  • Conveyor Belts
  • +
+

I'd like to learn to spin a dragon staff someday.

+
+
\ No newline at end of file diff --git a/articles/spinning.template.html b/articles/spinning.template.html new file mode 100644 index 0000000..d4e824f --- /dev/null +++ b/articles/spinning.template.html @@ -0,0 +1,9 @@ +{% extends "articles/article.template.html" %} + +{% block title -%} +I spin things around! +{%- endblock %} + +{% block body -%} +{%- include "articles/spinning.article.html" -%} +{%- endblock %} \ No newline at end of file diff --git a/articles/tamari.article.html b/articles/tamari.article.html new file mode 100644 index 0000000..6d97300 --- /dev/null +++ b/articles/tamari.article.html @@ -0,0 +1,66 @@ +
+

Tamari Lattices!

+

Tamari lattices are graphs (in the mathematical sense - a set of nodes with connections between them) describing a particular set of operations. I like them because they're pretty!

+
+ The (trivial) Tamari lattice for a three-element tree. +
A (trivial) Tamari lattice, generated by the associations of three elements. Source file.
+
+

Oh - oh dear. How'd that get there? Okay, that's not a very good example.

+

There are many ways to generate and think of a Tamari lattice. The way I prefer to think of it is this: Consider some binary operation - you take two elements to produce a new one. You want to combine several of these elements. So long as you have three or more elements, there are multiple ways to combine them.

+

The game we play is this: You're allowed to step from one way of combining the elements to another, but only by left-association: turning (a, (b, c)) into ((a, b), c).

+

The only remaining rule is that if you can step from one combination to another, the second one has to appear below the first one when you draw it.

+
+ A Tamari lattice for a four-element tree. +
As above, but generated by a four-element tree. Source file.
+
+

You can also think about it in terms of tree rotations - but remember that that's a different tree than the one we're building as the Tamari lattice. Tree rotations are just another way to think of left-association and right-association.

+

If you look at the wikipedia article on Tamari lattices, you'll see a very pretty image:

+ +

There are a lot of ways to reorganize a Tamari lattice, and it takes some artistic work to make one that really looks good. You can even visualize the same graph as a 3D shape called an "associahedron", but I like it in the simple gridded lattice form above. It reminds me how much beauty there can be in regularity - you can see the grid, but it doesn't look constrained by it. I might get a tattoo of the five-element Tamari lattice someday.

+
+ Tamari lattice for a five-element tree. +
Also the five-element lattice! Compare to the example above, on wikipedia.. Source file.
+
+

All these graphs with the ovals and the curvy lines were generated by yours truly! I made some python that would take a string representation of an association of a number of elements and convert it into an easily-manipulated memory representation. Then, a few tree rotations spit out all the possible results of left-associating on it, and it was relatively simple to print out a .dot file, parseable by graphviz, that described the graph. It even labeled the nodes!

+

dot happily converted them into the png files you're looking at. Of course, they don't have the human touch, so they're not organized into beautiful grid lines and 45° angles - but it can be fun to try to mentally reorganize them into a nicer shape. If you want, you can download the .dot source files for any of these, and play around with them in a graph-editing program (such as XDot)

+
+ Tamari lattice for a six-element tree. +
It's starting to get out of hand, I think.Source file.
+
+

Unfortunately, at a certain point I think it's going to get difficult to make these pretty. It turns out there are a lot of ways to associate a larger number of elements! Starting with a six-element tree or string, there are 42 elements (above). With seven, there are 132! Wowie!

+
+ Tamari lattice for a seven-element tree. +
Oh dear. Source file.
+
+

My server was chugging along trying to generate tamari_8.dot but I started getting messages from linode about going over my CPU quotas, so I canceled it - after it ran for an hour or so, without finishing. I think the seven-element one is messy-looking enough!

+

You can look at the python script I used to make this, but it's not particularly pretty - I was bored one day and decided I was going to figure out how to generate these... It's not exactly meant to be extensible. It's got a basic binary tree node class I threw together for working the association rules and a handful of (really ugly) helper functions. I just went through and rewrote it a bit to be nicer - the final output loop may please you if you enjoy sets and list comprehensions:

+
+current_generation = set()
+next_generation = {RootOfAllEvil}
+edges = set()
+labels = set()
+
+while next_generation: # While there are trees to examine.
+	# Move to look at the next generation.
+	current_generation = next_generation
+
+	# Clear out the next gen to fill it with the children of this generation.
+	next_generation = set()
+
+	# Ensure there are labels for this generation.
+	labels = labels | set(label_from_node(parent) for parent in current_generation)
+
+	for parent in current_generation:
+		children = generate_children(parent)
+
+		labels = labels | set(label_from_node(child) for child in children)
+		edges = edges | set(str(parent) + " -> " + str(child) + ";" for child in children)
+		next_generation = next_generation | children
+
+# Output a dot-formatted stream!
+args.output.write(u"digraph tamari {\n")
+args.output.write(u"\n".join(labels) + "\n")
+args.output.write(u"\n".join(edges))
+args.output.write(u"\n}\n")
+

The full script can be used by running python tamari.py --length 4 | dot -Tpng > output.png to produce a graph. tamari.py will print out to a specified file if you also include a filename: python tamari.py --length 5 output.dot

+
\ No newline at end of file diff --git a/articles/tamari.template.html b/articles/tamari.template.html new file mode 100644 index 0000000..8a25fec --- /dev/null +++ b/articles/tamari.template.html @@ -0,0 +1,9 @@ +{% extends "articles/article.template.html" %} + +{% block title -%} +Tamari Lattices: very pretty. +{%- endblock %} + +{% block body -%} +{%- include "articles/tamari.article.html" -%} +{%- endblock %} \ No newline at end of file diff --git a/articles/time_integration.article.html b/articles/time_integration.article.html new file mode 100644 index 0000000..0fec1ea --- /dev/null +++ b/articles/time_integration.article.html @@ -0,0 +1,35 @@ +
+
+

Time Integration

+
+

I find myself writing cooldowns and countdowns all the time. Bullets can only fire so fast, bombs explode after five seconds, enemies spawn every 10-15 seconds. Every time I do it, I write the same boilerplate in the initialization:

+
goomba.cooldown_max = 5 // 5 seconds to cool down
+goomba.cooldown_timer = goomba.cooldown_max
+

and the same boilerplate in the update function:

+
if goomba.cooldown_timer > 0:
+goomba.cooldown_timer -= time_step
+if goomba.cooldown_timer < 0:
+// insert end behavior here
+

and somewhere, I activate this behavior (in response to a keypress, or proximit, or contact):

+
goomba.cooldown_timer = goomba.cooldown_max
+

I do this kind of thing so often that I feel like it should be one of the basic functionalities offered when I'm trying to describe game entity behavior.

+
// initialization
+goomba.shot_timer = new CooldownTimer(length=5s, on_end=function() {...})
+// update functionality should be taken care of automatically, but if not:
+goomba.shot_timer.step(time_step)
+// activating the timer:
+goomba.shot_timer.start()
+

Now, wasn't that easy?

+

Of course, that's a pretty simple case! There are obvious extensions, though:

+
goomba.blink_timer = new EndlessTimer()
+goomba.blink_timer.interval = 5s
+goomba.blink_timer.at_progress(0%, goomba.change_color) // change color at the start of each interval
+goomba.blink_timer.at_progress(50%, goomba.take_step) // take a step once every interval halfway through
+

And now making timed behavior for our goomba is easy! We can even speed up the goomba easily: goomba.blink_timer.interval = 3s

+

Ideally, you don't need to even specify when to step your timers - that should be taken care of automatically. Of course, that means we'd want to register these timers instead of simply declaring them as a member - but maybe that's a matter of a grander architectural problem.

+

Although! If we do let the developer take care of stepping the timers, then we can also use it for things that aren't time-dependent:

+
player.limit_bar = new Meter(length=100) // general verison of the CooldownTimer
+player.on('hit by enemy', player.limit_bar.step)
+player.limit_bar.at_progress(100%, player.limit_break)
+

And now we've got a way for limit breaks to engage. Assuming there's a pre-existing event system and everything is architected carefully, it's now incredibly easy to fill meters in response to events.

+
\ No newline at end of file diff --git a/pages/auriga.html b/pages/auriga.html deleted file mode 100644 index ef35cde..0000000 --- a/pages/auriga.html +++ /dev/null @@ -1,305 +0,0 @@ - - - - - How should this website be laid out? - - - - - - -
-

Shit, I better make this server work.

-

On the layout of folders and files!

-

Basically I'm not sure how I should lay out this website. The current organization looks something like this:

-
    -
  • server/ (server root) -
      -
    • main_page.html
    • -
    • pages/ (articles with nice links) -
        -
      • auriga.html (accessed as http://example.com/auriga/)
      • -
      • language_for_games.html (http://example.com/language-for-games/)
      • -
      • tamari.html (http://example.com/tamari/) -
      -
    • -
    • static/ -
        -
      • stylesheet.css (global stying)
      • -
      • favicon.ico
      • -
      • banner.png
      • -
      • tamari/ (files for an article) -
          -
        • figure_1.png
        • -
        • figure_2.png
        • -
        • experiment_data.csv
        • -
        -
      • -
      -
    • -
    -
  • -
-

This has a number of problems. First and foremost, barely any thought has gone into naming of folders and files - which is because not much thought has gone into their placement and hierarchy.

-

The server is written using Flask, and this is the rundown on how it works when a user goes to http://example.com/some-path/:

-
    -
  1. If the path requested is the root of the website, render main_page.html.
  2. -
  3. If the path requested begins with static/, render a file from the static content directory.
  4. -
  5. Else, convert the path (some-path) into the filename (pages/some_path.html) assuming it's an article. If it exists, render it.
  6. -
  7. If there's no article by that name, display the miscellaneous articles page. Or 404. The current behavior isn't very high on my priorities.
  8. -
-

There are a bunch of problems with this system - it's kind of brittle, file paths have the meaningless-to-the-user static/ at the beginning, and it means that when I include a resource in http://example.com/tamari/ (the file pages/tamari.html) I have to use a path like ../static/tamari/figure_1.png - when I should be able to reference files related to the Tamari article by a closely-related URL: ./figure_1.png.

-

All this points to the idea of having my articles be in folders, so it would look something like this:

-
    -
  • server/ (server root) -
      -
    • main_page.html
    • -
    • articles/ (articles with nice links) -
        -
      • auriga/ -
          -
        • auriga.html
        • -
        -
      • -
      • language-for-games/ -
          -
        • language_for_games.html
        • -
        -
      • -
      • tamari/ -
          -
        • tamari.html
        • -
        • figure_1.png
        • -
        • figure_2.png
        • -
        • experiment_data.csv
        • -
        -
      • -
      -
    • -
    • static/ -
        -
      • stylesheet.css (global stying)
      • -
      • favicon.ico
      • -
      • banner.png
      • -
      -
    • -
    -
  • -
-

And hell, at this point having the filenames match the folders (auriga/auriga.html) is just a pain, so we could name the main page in each folder index.html, and suddenly this looks very standard. So why don't I do this?

-

Well... I don't know. But something always bothered me about the index.html standard. Maybe it's just that my article about Tamari lattices isn't an index, so I don't think it should be called one. That's certainly part of it. Also, some of my articles and pages are going to be very short - in theory I'd like them to be able to be tiny snippets, which could be included on other pages. It'd be annoying to have a subfolder for every single article I write! But... maybe not too annoying. I'll have to think about that.

-
-

But what are we doing?

-

What would my ideal web framework be? Well, something like Flask, really. I like flask because this is easy:

-
-@app.route("/some-convoluted/url/<options>/with-stuff/")
-def render_shoofle_page(options=None):
-  if options == "bananas":
-return render_page("bananas.html")
-  else:
-return render_page("some_other_template.html", options=options)
-

And, lo and behold, I can go to example.com/some-convoluted/url/bananas/with-stuff/ and see the right generated page! I like having that capacity at my fingertips. In particular, it makes it easy to respond to various other kinds of requests. That's what I really like about Flask - I feel like I can do everything a webserver can do, but it all takes the same very small amount of work. This compares to, say, PHP, where it's easy to make pages... But more complicated behavior, especially anything involving dynamic URL paths, involves jumping through hoops.It might seem like an overblown complaint, but the integrity of my mental models is important to me - and in PHP, I don't understand what gets sent to the requestor. If I make a Flask route returning JSON, I know it's exactly what a script wants. With PHP, I would hope that it wouldn't have weird headers or content type or additional markup. Plus, you have to work with PHP, which I would wish on no one.

-

I don't like Flask because I need to specify all the behaviors, and I'm worried that I'll get it wrong (thus, this article).

-

My ideal world solution would be something that lets me pass requests for subpaths to other python modules:

-

    -
  • server/ -
      -
    • server.py
    • -
    • main_page.html
    • -
    • articles/ -
        -
      • articles.py
      • -
      • auriga.html
      • -
      • language-for-games.html
      • -
      • tamari.html
      • -
      -
    • -
    • games/ -
        -
      • games.py
      • -
      • scores.csv
      • -
      • play_the_game.html
      • -
      • game.js
      • -
      -
    • -
    -
  • -
-

Aaaand how it works would be simple:

-
    -
  1. A request comes in to http://example.com/tamari/, and it is handled by server.py.
  2. -
  3. server.py says "Okay, I don't have any specific handlers that do anything with this URL, but I think articles/articles.py does!" and sends the request to articles.py.
  4. -
  5. articles.py receives a request for /tamari/ and has a rule for handling that, and so it renders the appropriate HTML file.
  6. -
-

The basic idea is that requests can be routed to other python modules. If those modules can be reloaded easily (or automatically!), this would mean I could write complicated URL handling in subdirectories of my website - without having to touch any of the code that handles anything else. It means I can modularize - and that's awesome.

-
    -
  1. An HTTP GET request comes in to http://example.com/games/points, and server.py takes it.
  2. -
  3. server.py doesn't know what to do with it, but it knows that requests for URLs starting with /games should be handled by games/games.py.
  4. -
  5. games.py receives an HTTP GET request for /games/points, and so it can respond to this with a JSON object that tells the requestor how many points they got.
  6. -
-

This is the main point of the tiered handlers system - that I can write handlers for all kinds of requests, without having to interact with other parts of my website.

-

Basically, I want to combine the simple directory structure of PHP websites with the principles of Flask I really like - separating content from server logic, keeping things simple, and having control.

-

Here's how I imagine this would work:

-
-from flask import Flask, render_template, redirect
-
-app = Flask(__name__)
-
-def main_page(): return render_template("main.html")
-app.add_url_rule("/", main_page)
-
-import games
-app.add_module_url_rule("/games/<path:rest>", games)
-

As usual, don't think too hard about the syntax I just proposed. The point is to distribute URL routing logic throughout multiple files, so that my entire website isn't controlled by one brutalistic overlord server.py file that decrees the entire path of every request. I want things split up, dammit!

-
-

Now I'm reading up on the Flask docs, and it turns out that it already has the capacity for this. Unfortunately, it's complicated and I'm whiiiny.

-

It seems that Flask provides the developer with three options:

-
    -
  • Package Organization: Essentially, break up your server code, like configuration and URL route assignment, into separate files. This doesn't actually solve any of my issues - most of what this does (as far as I can tell) is separate the server initialization logic from the routing and response logic. Plus, it makes it easier to use your application in something else. More on this soon.
  • -
  • Blueprints: Very close to what I want. I could define a Blueprint object in articles.py. This Blueprint object would register all the routes and endpoints that I want to register - and then, when the server starts, it gets all of those from the blueprint. This means we can actually factor views out. I'm really not sure, though, what this affords that the organizing your application as a package doesn't.
  • -
  • Application Dispatching: The most powerful of the three! This is a slightly lower-level solution, wherein you actually run multiple applications concurrently, and dispatch requests to them. This is kind of overkill, but at least it's nice to know that it's possible - things like the async_app example from previous could really make sense, if the app is large enough in scope. Application dispatching means you can just pull in another application and route things to it on the WSGI layer. Or something.
  • -
-

Okay, but... how do they score up? I have some requirements:

-
-
Organization
-
I want to define the behavior for example.com/articles/tamari/ in the same place as example.com/articles/language-for-games/ is defined, but in a different file from where example.com/games/hunt-the-wumpus is defined.
-
Simplicity
-
Defining a URL route and endpoint for each subfolder of my application shouldn't be much more complicated than defining behaviors in a flat system. Ideally, registering all the behaviors for the articles/ subpath is no more complicated than app.route("/articles/", articles_handler)
-
Ease of Extension
-
I'm writing a family of pages. They're starting to get a little too complicated. It should be really easy to go from that situation to having a nicely-delineated subsection of my website.
-
Do I Need to Restart the Server?
-
I hate restarting the server. Maybe this is a little thing, but gah! I want the server to detect changes automatically! This shouldn't be that hard. Of course, this is a much thornier topic when it comes to subpages that might store their own state... So it might be out the window unless I want to get my hands way more dirty.
-
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

How do they fare up?

OrganizationSimplicityEase of ExtensionNo Restart Required?
Current
Organize as a Package
Blueprints
Application Dispatch
-
-

In light of this chart, it seems like Blueprints are the clear option. Also, "organize as package" is really vague. What they suggested didn't sound very helpful - but making python packages (finally learning how to use __init__.py files!) will probably get used in the solution I'm thinking I'll use, which looks something like this:

-
    -
  • server/ -
      -
    • server.py
    • -
    • articles/ -
        -
      • article_routing_blueprint.py
      • -
      • main_page.html
      • -
      • auriga.html
      • -
      • language-for-games.html
      • -
      • tamari.html
      • -
      • tamari_files
      • -
      -
    • -
    • games/ -
        -
      • games_blueprint.py
      • -
      • scores.csv
      • -
      • play_the_game.html
      • -
      • game.js
      • -
      -
    • -
    • static/ -
        -
      • favicon.ico
      • -
      • stylesheet.css
      • -
      • lol_a_cat.jpg
      • -
      -
    • -
    -
  • -
-

If there's static content that is globally important, with simple routing rules, it can - of course - be stored in the server root's static/ directory. Static file handlers don't take much configuration, so this can go in the root.

-

All the files related to articles are contained in the articles/ directory, or subdirectories. article_routing_blueprint.py contains a blueprint definition that tells the server how to route calls that result in the rendering of the various articles I've written.

-

All files related to the little javascript games I've made, on the other hand, go in the games/ directory. State for this application is managed by games_blueprint.py, which is sort of like a very tiny application. I think it's possible to keep track of state, if you remember to keep variables global... This is tricksy, but should be doable - and if it gets larger, it should almost certainly be developed as a separate application entirely. At that point, I should figure out how to include multiple applications in one website!

-

I want to finish this bit by writing down some code for the base server.py logic:

-
-from flask import Flask
-
-# app configuration and initialization
-app = Flask(__name__)
-app.debug = True
-
-# content in articles/
-import articles
-app.register_blueprint(articles.blueprint)
-
-# content in games/
-import games
-app.register_blueprint(games.blueprint, url_prefix="/games")
-
-

Okay, one last thing.

-

This has been a lot about how to get some basic extensible functionality working. But wait, someone cries in the distance! Why are we going to such lengths to do all this in the first place, when you're just serving static HTML files? Isn't this way more work than you need?In case you're reading this article long after I've finished these changes (I hope so, 'cause I should get to work on this immediately), at this point all the articles were static HTML files. That voice speaks the truth.

-

Thanks for asking. The answer is that I want to change how this site is structured, and the articles are the specific thing I want to change. See, as it is, each article has basically the same header at the top, and basically the same footer at the bottom, and... Well, they're just all enclosed in the same stuff, and so when I change the styling of the website - or, horror of horrors, try to add a sidebar across the hwole site - I have to change all of them! And, as I think we've been over before, I'm lazy.

-

Conveniently, as I've been obsessively reading discussions of standards-compliant HTML5 and document formats and such lately, this laziness messes with my indomitable enthusiasm for conceptually pleasing solutions. Here's what I've got:

-
    -
  • Articles on my site are blocks of text, consisting of my rambling on some topic. Something I've written down. They can have some amount of associated content, but for the most part they're just text. So, I'm going to reduce them down to their bare minimum of unique content - in many cases, just an <article> tag containing what I wrote!
  • -
  • But sometimes I want to have multiple articles display in one page! Sometimes my articles are single paragraphs. Sometimes they can be grouped into related, uh, groups! What do we do then? The answer is templates. Flask uses Jinja2, a perfectly nice templating engine - and why not use it?
  • -
  • If I want to display the articles contained in articles/parkour_is_awesome.html, articles/exercise.html, and articles/how_is_body.html), then I just make a new page, a Jinja2 template, which contains this: -
    -{% include "articles/parkour_is_awesome.html" %}
    -{% include "articles/exercise.html" %}
    -{% include "articles/how_is_body.html" %}
    -
  • -
-

And ta-da! We now have a page which has those three articles in a row.

-

This, of course, also means we have a neat format for how to display things in general. Someone requested access to a URL! Find the file it points to. If it's an HTML fragment, then point the general page template to that file, and render. The general page template is just a template that adds the header, footer, sidebar, universal styling, and so on, as required, for universality.

-

So if you go to example.com/tamari/, the server says "Okay, you want to look at the article in the HTML fragment file articles/tamari.html." It renders the general page template around that article, and returns that to the user.

-

Because I like having the option to look at these things many ways, I'm probably also going to allow for a user to do python articles.py --generate [path], which will simply spit out the the rendered results of going to [path]. I guess that might just be indistinguishable from using wget, but hey. It can't hurt to give more functionality!

-

It remains to be seen precisely how many pages on this site will use the general page template. I don't want to rely on it too much, but, man, is there any reason not to? Basically, it means that I don't have to worry about writing the headers of anything on my website. I guess I'll need to be able to throw it off when I'm making subpages that are intended to be stylistically distinct from the rest of my site.

-
- - diff --git a/pages/language_for_games.html b/pages/language_for_games.html deleted file mode 100644 index a8df847..0000000 --- a/pages/language_for_games.html +++ /dev/null @@ -1,150 +0,0 @@ - - - - - A Language for Games - - - - - -

I like making games but sometimes I spend a lot of time doing stuff that is just annoying. What if a language or environment were designed to make those things easy?

-
-
-
-

Snorlax Evaluation

-

(define-by-reference, or: lazy evaluation taken to extremes)

-
-

Okay, so I've got a Bomb entity in my game. It's already got the behavior for flying toward its destination. What if I want to make a SmartBomb that tracks a target even if they move? I might do it something like this:

-
SmartBomb.on_update = function (timestep) {
-  // other update behaviors go in this function as well
-  this.destination = target.position
-}
-

I have to explicitly tell my engine to execute this every frame. I think in terms of this kind of thing already, because I'm a math and physics person. Why can't I define relationships like this more simply? What if, in fact, this was the default way we referred to values? It's worth noting that what I've thought of here is essentially just functional reactive programming. You can go read wikipedia if you want.

-
SmartBomb.destination = target.position
-

Any time something fetches the value of SmartBomb.destination it'll fetch the value of target.position. Writing it this way, instead of explicitly specifying which variables need to be kept up-to-date with other variables (and when they need to be updated!), means it's easier to give variables interesting dependencies on other values - like this shield which should float a certain distance between the player and the enemy:

-
shield.position = player.position + shield.distance * unit_vector(enemy.position - player.position)
-

If and when you do need to store something's value at a specific time, you can do that if you want - for example, this code when a bomb explodes:

-
explosion_location = current_value(bomb.position)
-// or maybe the syntax is like this:
-explosion_location = bomb.position.current_value
-

So far, everything's time-independent. For example, the shield object's position depends only on the positions of other objects.

-

Why not include time-dependence? A bullet has a position and a velocity, and these are related in a way that doesn't change at any point during gameplay. Why not codify that relationship in a similar way?

-
bullet.position = bullet.position + time_step * bullet.velocity
-// or maybe
-bullet.position = bullet.position + bullet.position.time_since_last_calculation * bullet.velocity
-

Okay, if this seems like impossible magic, I'm sorry. Let's walk through it:

-
    -
  1. The global loop asks for bullet.position in order to know where it should be drawn.
  2. -
  3. bullet.position looks at its cached value and sees that it's out of date.
  4. -
  5. bullet.position subtracts timestamps to find the time step between now and whenever it last got calculated, and remembers this as time_step
  6. -
  7. The time-dependence expression is now evaluated, using the last calculated value for bullet.position, and fetching bullet.velocity as normal.
  8. -
-

Hell, why not remember the entire history of a variable's values?Performance. When I fetch the last calculated value for bullet.position, why not go ahead and make that a graph-crawling reference-calculation just like everything else?Reactive time-dependence is something to think about for an interesting mechanic, if nothing else - keeping track of dependencies on history means you could change a value at any point throughout the game's history and see how it changes things... Just like Achron! And the flaws that make it seem infeasible here wouldn't be a problem in, say, a turn-based time game.

-

Performance is a legitimate worry at that point. If you include the full-on time-dependence, then depending on implementation you might end up following a reference back through every frame the game's been through. This approach would obviously fall apart because every value in your world is going to be at least O(t) to calculate, which sucks - there should be no time-dependent complexity - but there are solutions:

-
    -
  • Careful design can ensure that your objects aren't too densely interlinked.
  • -
  • If you know you can do things more efficiently in some strenuous case, you can always hard-code it to extract current values and explicitly pass them around whenever you want.
  • -
  • Variables should carry metadata about when they last changed, and when their predecessors and descendents in the graph have changed - so that, in calculating the shield's position, you don't re-fetch the enemy.position, if you know it hasn't changed.
  • -
-

Besides which, we have fast computers, and without running into hard algorithmic complexity problems (collision detection, I'm lookin' at you!) it's not that easy to overload a modern gaming machine. And hey, maybe having declarative time-dependent values is a pipe dream - I don't care. The core idea is still awesome.

-

Here's an example (with more attention paid to data types) of how I'd come up with a vector that always points to a set offset from the position of bullet.

-
bullet.position = Wrapper(v(1,2))
-offset = v(3,4)
-endpoint = bullet.position + offset
-print endpoint
---> WrapperSum(bullet.position, offset)
-print endpoint.current_value
---> v(4,6)
-bullet.position.current_value = v(-10,0)
-print endpoint.current_value
---> v(-7,4)
-

Once I've started thinking and talking about this, I start wondering how much we need imperative game loops at all, for anything other than those troublesome time dependencies and feedback loops. I wonder...

-
print bullet.contacts
---> Wrapper(List<Contacts>)
-contact_acceleration = sum(contact.force_to_resolve for contact in bullet.contacts)
-bullet.acceleration = contact_acceleration
-

Assuming that force due to contacts is the only thing present in the world, then the bullet now feels enough acceleration to resolve all the conflicts. I mean, assuming contacts are simple to work with, and they always are. Right? Right?

-

I'm thinking about this a lot because this kind of referencing is very natural for things like defining interlinks between components in my game engine - I've got conflicting interests:

-
    -
  • I want to keep my vectors and properties lightweight, so that a bullet's position should just be a vector, with which I should be able to do math as freely and easily as integers.
  • -
  • But I also want to ensure that the collision component's position is the same as the bullet's position! I'd like to do collision_comp.position = bullet.position
  • -
  • And then I run into the problem that since I want my vectors as primitive as possible, if I change bullet.position then it's not going to change collision_comp.position because pass-by-value is the dominant paradigm.
  • -
-

The solution I've been working with is wrap together sets of related data (as components). The player object has a PositionComponent and a CollisionComponent, and the CollisionComponent stores a reference to the player's PositionComponent so that it can access it at any time. Actually holding those references constant means that there would be problems if the player.position_component suddenly changed to a different object - but that should never happen. I'm satisfied with this for the reasons I implemented it in the first place, but it doesn't get me the dynamic interlinking I'd like from something more like functional reactive programming.

-

So I don't know. I would love to have the capacity for functional reactive programming in my game engines, at the least! It's so useful for making bulletproof dynamic interactive systems.

-
-
-
-
-

Time Integration

-
-

I find myself writing cooldowns and countdowns all the time. Bullets can only fire so fast, bombs explode after five seconds, enemies spawn every 10-15 seconds. Every time I do it, I write the same boilerplate in the initialization:

-
goomba.cooldown_max = 5 // 5 seconds to cool down
-goomba.cooldown_timer = goomba.cooldown_max
-

and the same boilerplate in the update function:

-
if goomba.cooldown_timer > 0:
-  goomba.cooldown_timer -= time_step
-if goomba.cooldown_timer < 0:
-  // insert end behavior here
-

and somewhere, I activate this behavior (in response to a keypress, or proximit, or contact):

-
goomba.cooldown_timer = goomba.cooldown_max
-

I do this kind of thing so often that I feel like it should be one of the basic functionalities offered when I'm trying to describe game entity behavior.

-
// initialization
-goomba.shot_timer = new CooldownTimer(length=5s, on_end=function() {...})
-// update functionality should be taken care of automatically, but if not:
-goomba.shot_timer.step(time_step)
-// activating the timer:
-goomba.shot_timer.start()
-

Now, wasn't that easy?

-

Of course, that's a pretty simple case! There are obvious extensions, though:

-
goomba.blink_timer = new EndlessTimer()
-goomba.blink_timer.interval = 5s
-goomba.blink_timer.at_progress(0%, goomba.change_color) // change color at the start of each interval
-goomba.blink_timer.at_progress(50%, goomba.take_step) // take a step once every interval halfway through
-

And now making timed behavior for our goomba is easy! We can even speed up the goomba easily: goomba.blink_timer.interval = 3s

-

Ideally, you don't need to even specify when to step your timers - that should be taken care of automatically. Of course, that means we'd want to register these timers instead of simply declaring them as a member - but maybe that's a matter of a grander architectural problem.

-

Although! If we do let the developer take care of stepping the timers, then we can also use it for things that aren't time-dependent:

-
player.limit_bar = new Meter(length=100) // general verison of the CooldownTimer
-player.on('hit by enemy', player.limit_bar.step)
-player.limit_bar.at_progress(100%, player.limit_break)
-

And now we've got a way for limit breaks to engage. Assuming there's a pre-existing event system and everything is architected carefully, it's now incredibly easy to fill meters in response to events.

-
-
-
-

Easy Peasy State Machinesy

-
-

Why do I always have to actually use boolean flags (manually setting and unsetting them) to track the states of my entities?

-
-player.shielding = new Toggle()
-keyboard.on('w pressed', player.shielding.next)
-

I'm just throwing this syntax together off the top of my head. The syntax isn't important - what's important is that I should be able to describe this and have it happen.

-
-strobelight.color = new Sequence(['red', 'green', 'blue'])
-strobelight.update = function(dt) {
-  strobelight.color.next()
-  strobelight.draw()
-}
-

Seriously, don't worry about this syntax - it's ugly and I just threw it together. The point is the kinds of things I should be able to do. This is all totally feasible.

-
-goomba.ai = new StateMachine(['sleep', 'suspicious', 'alert'])
-goomba.ai['sleep'].transitions_to['suspicious'].on(goomba.near(player))
-// the state 'sleep' should transition to 'suspicious' on the event goomba.near(player)
-goomba.ai['sleep'].update = goomba.draw_snores
-
-goomba.ai['suspicious'].transitions_to['alert'].on(goomba.sees(player))
-goomba.ai['suspicious'].update = goomba.search_for_player
-
-// shit we forgot to make a state for just patrolling
-goomba.ai.add_state('patrol')
-goomba.ai['patrol'].transitions_to['sleep'].after(5 seconds)
-goomba.ai['patrol'].update = goomba.wander
-
-// oh hey, she should occasionally wake up to patrol!
-goomba.ai['sleep'].transitions_to['patrol'].after(5-10 seconds)
-

So y'know. State machines are powerful. In order to really use them, you need a robust event system, or else they're a huge pain - it's still possible, you just... need to code it to check the conditions on each update loop. That could get expensive!

-

For the example of checking goomba.near(player) - which has an optional argument for the distance - that function checks if there's already a collision detection object attached to goomba for setting off proximity events. If there is, it returns the event handle, which can be used for binding behaviors to the firing of that event. If a proximity collider of the right size doesn't exist, then it creates one, and returns the event handle.

-

-
- - diff --git a/pages/tamari.html b/pages/tamari.html deleted file mode 100644 index 882e280..0000000 --- a/pages/tamari.html +++ /dev/null @@ -1,87 +0,0 @@ - - - - - Tamari Lattices - - - - - - -
-

Tamari Lattices!

-

Tamari lattices are graphs (in the mathematical sense - a set of nodes with connections between them) describing a particular set of operations. I like them because they're pretty!

-
- The (trivial) Tamari lattice for a three-element tree. -
A (trivial) Tamari lattice, generated by the associations of three elements. Source file.
-
-

Oh - oh dear. How'd that get there? Okay, that's not a very good example.

-

There are many ways to generate and think of a Tamari lattice. The way I prefer to think of it is this: Consider some binary operation - you take two elements to produce a new one. You want to combine several of these elements. So long as you have three or more elements, there are multiple ways to combine them.

-

The game we play is this: You're allowed to step from one way of combining the elements to another, but only by left-association: turning (a, (b, c)) into ((a, b), c).

-

The only remaining rule is that if you can step from one combination to another, the second one has to appear below the first one when you draw it.

-
- A Tamari lattice for a four-element tree. -
As above, but generated by a four-element tree. Source file.
-
-

You can also think about it in terms of tree rotations - but remember that that's a different tree than the one we're building as the Tamari lattice. Tree rotations are just another way to think of left-association and right-association.

-

If you look at the wikipedia article on Tamari lattices, you'll see a very pretty image:

- -

There are a lot of ways to reorganize a Tamari lattice, and it takes some artistic work to make one that really looks good. You can even visualize the same graph as a 3D shape called an "associahedron", but I like it in the simple gridded lattice form above. It reminds me how much beauty there can be in regularity - you can see the grid, but it doesn't look constrained by it. I might get a tattoo of the five-element Tamari lattice someday.

-
- Tamari lattice for a five-element tree. -
Also the five-element lattice! Compare to the example above, on wikipedia.. Source file.
-
-

All these graphs with the ovals and the curvy lines were generated by yours truly! I made some python that would take a string representation of an association of a number of elements and convert it into an easily-manipulated memory representation. Then, a few tree rotations spit out all the possible results of left-associating on it, and it was relatively simple to print out a .dot file, parseable by graphviz, that described the graph. It even labeled the nodes!

-

dot happily converted them into the png files you're looking at. Of course, they don't have the human touch, so they're not organized into beautiful grid lines and 45° angles - but it can be fun to try to mentally reorganize them into a nicer shape. If you want, you can download the .dot source files for any of these, and play around with them in a graph-editing program (such as XDot)

-
- Tamari lattice for a six-element tree. -
It's starting to get out of hand, I think.Source file.
-
-

Unfortunately, at a certain point I think it's going to get difficult to make these pretty. It turns out there are a lot of ways to associate a larger number of elements! Starting with a six-element tree or string, there are 42 elements (above). With seven, there are 132! Wowie!

-
- Tamari lattice for a seven-element tree. -
Oh dear. Source file.
-
-

My server was chugging along trying to generate tamari_8.dot but I started getting messages from linode about going over my CPU quotas, so I canceled it - after it ran for an hour or so, without finishing. I think the seven-element one is messy-looking enough!

-

You can look at the python script I used to make this, but it's not particularly pretty - I was bored one day and decided I was going to figure out how to generate these... It's not exactly meant to be extensible. It's got a basic binary tree node class I threw together and a handful of (really ugly) helper functions. I just went through and rewrote it a bit to be nicer - the final output loop may please you if you enjoy sets and list comprehensions:

-
-current_generation = set()
-next_generation = {RootOfAllEvil}
-edges = set()
-labels = set()
-
-while next_generation: # While there are trees to examine.
-  # Move to look at the next generation.
-  current_generation = next_generation
-
-  # Clear out the next gen to fill it with the children of this generation.
-  next_generation = set()
-
-  # Ensure there are labels for this generation.
-  labels = labels | set(label_from_node(parent) for parent in current_generation)
-
-  for parent in current_generation:
-    children = generate_children(parent)
-
-    labels = labels | set(label_from_node(child) for child in children)
-    edges = edges | set(str(parent) + " -> " + str(child) + ";" for child in children)
-    next_generation = next_generation | children
-
-# Output a dot format stream!
-args.output.write(u"digraph tamari {\n")
-args.output.write(u"\n".join(labels) + "\n")
-args.output.write(u"\n".join(edges))
-args.output.write(u"\n}\n")
-

The full script can be used by running python tamari.py --length 4 | dot -Tpng > output.png to produce a graph. tamari.py will print out to a specified file if you also include a filename: python tamari.py --length 5 output.dot

-
- - diff --git a/project_list.html b/project_list.html index 3cea239..0f57ab7 100644 --- a/project_list.html +++ b/project_list.html @@ -4,9 +4,9 @@ Projects, by Shoofle - - - + + +
@@ -93,10 +93,10 @@

Hi! I'm Shoofle. I'm a lot of person.

-

I graduated from the University of Virginia in 2012, majoring in math and physics with a minor in computer science. I'm really good at math. I'm pretty good at programming.

-

I've always loved to draw, but never focused enough time on it. I'm an intensely creative person and I like to think that this comes out in everything I do - and I'm trying harder every day.

-

I'm a passionate advocate for feminism, transgender rights, queer rights, and social justice in general.

-

Warning! This website is still very much under construction. I'm in the process of figuring out how I want this website to look and function.

+

I graduated from the University of Virginia in 2012, majoring in math and physics with a minor in computer science. I'm really good at math. I'm pretty good at programming.

+

I've always loved to draw, but never focused enough time on it. I'm an intensely creative person and I like to think that this comes out in everything I do - and I'm trying harder every day.

+

I'm a passionate advocate for feminism, transgender rights, queer rights, and social justice in general.

+

Warning! This website is still very much under construction. I'm in the process of figuring out how I want this website to look and function.

diff --git a/pure_flask.py b/pure_flask.py index 4b0c3d3..9ac91b8 100644 --- a/pure_flask.py +++ b/pure_flask.py @@ -1,21 +1,19 @@ import os from os.path import join, isfile -from flask import Flask, render_template, url_for, redirect -app = Flask(__name__) -app.template_folder = "." -server_directory = "/home/shoofle/auriga/" -project_directory = join(server_directory, "pages") -guitar_directory = join(server_directory, "guitar") +from flask import Flask, render_template, url_for, redirect, send_from_directory -@app.route("/") -def project_list(): - return render_template("project_list.html") +from articles import bloop -@app.route("/miscellany/") -def miscellany(): - guitar_files = [ f for f in os.listdir(guitar_directory) if isfile(join(guitar_directory, f)) and "html" not in f ] - return render_template("pages/miscellany.html", guitar_files=guitar_files) +app = Flask(__name__) +app.template_folder = "" +#server_directory = "/home/shoofle/auriga/" +#project_directory = join(server_directory, "pages") +#guitar_directory = join(server_directory, "guitar") + +@app.route("/favicon.") +def favicon(extension=None): + return send_from_directory(join(app.root_path, "static"), "favicon.png", mimetype="image/png") @app.route("/guitar_tab/") @app.route("/guitar_tab/") @@ -27,15 +25,8 @@ def guitar_display(file_name=None): return render_template("guitar/render_tab.html", contents=c) return render_template("guitar/default.html", guitar_files=guitar_files) -@app.route("//") -def unknown_project(project_name="miscellany"): - for file_name in os.listdir(project_directory): - if not isfile(join(project_directory, file_name)): - continue - if file_name == project_name.replace("-","_") + ".html": - return render_template("pages/" + file_name) - else: - return redirect(url_for('miscellany') + '#' + project_name) +app.register_blueprint(bloop) if __name__ == "__main__": + app.debug = True app.run() diff --git a/pure_flask.pyc b/pure_flask.pyc index 03ad58762d38fb7ea9db2ca29c091f5e32970c25..c6929ec2c76a26fd927c4dcd56d45b3a8b54042b 100644 GIT binary patch literal 1512 zcmaJ>&2H345U#c}f5{992?UUkxcUH@D~BjT0&$Flv(m0CEtY58GkD^*O?Mj+DLI8Z zCmwWd9+3ha=Lv5_d?~M5>Z1&!StXxcG+OX%s;948^K0D&J>^TNI^^dhB5FvdrAM z--Tv%m1~=NZJILk+7DoBZJ91=>xbgc3#;eeA7J4SoGxr_(zUU+7mgr_9!TjM2|iqYyI!F<$Z^MAkH(gKS;` zxoLz2iEtBWO>1?!V9bu13xTLRo6SMK^FOZ8JJ*GACvLj-KoC>|( zcvCvAqVPu(GxAoWi-tjcCb93&kA7r+DW95bnl%k?5_OTP|c06Y8`DHm}|P&ZNS z1oX0S9>nu|KLT%f7&EQbP3E504UQ5nS0G`o<-S&DdCU}84+5vZX$csJReT##Sb`3U zY5rR(aR0k2=Y_)rUF^dmRG{#=8ms&2u6m&SHmp2Vjn#|dy)b5)2`Xu7vZ6}Uh}jbJ zZn<{tj+mNKkd!7PCKQYr5h)`Uc5?%S;*u}3m=J&$bMf5?Ou00Td2D3nH^Yu0FERX* z+SVRlH)owTjdi%ozUaRknY&hxw literal 2009 zcmbVM!A{#i5S?`(At519D4|lTM(rh+;BzH}Dz$2RLaI4bN>#0_cp)2{wefCRBGD6k zLjR~=&|m2fv@;Wy_`o3{iJkTH&hwkM-uLHkt8w|}_vazpeb({+EgrTe022HOxBwW; z#{pajNCOxIkXB$&0igm=Yj7iAuntKD)H=v2Bx^9J(O7RlvQBrCD%=83t9!i0_=d+D z9M+D70M*4kWNkVG<)r?k&V^R$y8f+IC7J;M^A|8)Wf56 zvMs#Kr-hPdr0y`Exh5ecf(^X3^h^ktJe+8gjANbll{wc&Ei3HBg4f=2$T`Q#A}dHX z87tE2wUHa4&9yPwoG&YCzbwiZudIeGx_k0=NzyJ!8frJ|XQuB)s$b~us#sRr%d$3N zk(rUmBAI1nk!wgtEPEw^QTa|Tw=MrdX%t5`SX%6THf%;?++7C)NvTm`VVP$(Key2&fD`B4loR^0XiQ)UER`F z)fj96MacVaU*V@fyya-X2Ic>Ue4{O!mXbJN;dREUhU7b+HXq z@k-Rh3$Yup3MnVCHd6K|R3nN?4swplh>8}1B|lzDN68fDIp@3&cvPQa;Jb1*&-=@Y z8B-S7)TxMkipb}PjEcxYwl|_4qYj>!zL=UtpFDb?S#t4ck|onr9n-66=`C)G{h%r8 Pq8c2mExt|BZnVUI*&~Cc diff --git a/static/auriga.html b/static/auriga.html new file mode 100644 index 0000000..17ad43e --- /dev/null +++ b/static/auriga.html @@ -0,0 +1,305 @@ + + + + + How should this website be laid out? + + + + + + +
+

Shit, I better make this server work.

+

On the layout of folders and files!

+

Basically I'm not sure how I should lay out this website. The current organization looks something like this:

+
    +
  • server/ (server root) +
      +
    • main_page.html
    • +
    • pages/ (articles with nice links) +
        +
      • auriga.html (accessed as http://example.com/auriga/)
      • +
      • language_for_games.html (http://example.com/language-for-games/)
      • +
      • tamari.html (http://example.com/tamari/) +
      +
    • +
    • static/ +
        +
      • stylesheet.css (global stying)
      • +
      • favicon.ico
      • +
      • banner.png
      • +
      • tamari/ (files for an article) +
          +
        • figure_1.png
        • +
        • figure_2.png
        • +
        • experiment_data.csv
        • +
        +
      • +
      +
    • +
    +
  • +
+

This has a number of problems. First and foremost, barely any thought has gone into naming of folders and files - which is because not much thought has gone into their placement and hierarchy.

+

The server is written using Flask, and this is the rundown on how it works when a user goes to http://example.com/some-path/:

+
    +
  1. If the path requested is the root of the website, render main_page.html.
  2. +
  3. If the path requested begins with static/, render a file from the static content directory.
  4. +
  5. Else, convert the path (some-path) into the filename (pages/some_path.html) assuming it's an article. If it exists, render it.
  6. +
  7. If there's no article by that name, display the miscellaneous articles page. Or 404. The current behavior isn't very high on my priorities.
  8. +
+

There are a bunch of problems with this system - it's kind of brittle, file paths have the meaningless-to-the-user static/ at the beginning, and it means that when I include a resource in http://example.com/tamari/ (the file pages/tamari.html) I have to use a path like ../static/tamari/figure_1.png - when I should be able to reference files related to the Tamari article by a closely-related URL: ./figure_1.png.

+

All this points to the idea of having my articles be in folders, so it would look something like this:

+
    +
  • server/ (server root) +
      +
    • main_page.html
    • +
    • articles/ (articles with nice links) +
        +
      • auriga/ +
          +
        • auriga.html
        • +
        +
      • +
      • language-for-games/ +
          +
        • language_for_games.html
        • +
        +
      • +
      • tamari/ +
          +
        • tamari.html
        • +
        • figure_1.png
        • +
        • figure_2.png
        • +
        • experiment_data.csv
        • +
        +
      • +
      +
    • +
    • static/ +
        +
      • stylesheet.css (global stying)
      • +
      • favicon.ico
      • +
      • banner.png
      • +
      +
    • +
    +
  • +
+

And hell, at this point having the filenames match the folders (auriga/auriga.html) is just a pain, so we could name the main page in each folder index.html, and suddenly this looks very standard. So why don't I do this?

+

Well... I don't know. But something always bothered me about the index.html standard. Maybe it's just that my article about Tamari lattices isn't an index, so I don't think it should be called one. That's certainly part of it. Also, some of my articles and pages are going to be very short - in theory I'd like them to be able to be tiny snippets, which could be included on other pages. It'd be annoying to have a subfolder for every single article I write! But... maybe not too annoying. I'll have to think about that.

+
+

But what are we doing?

+

What would my ideal web framework be? Well, something like Flask, really. I like flask because this is easy:

+
+@app.route("/some-convoluted/url/<options>/with-stuff/")
+def render_shoofle_page(options=None):
+	if options == "bananas":
+return render_page("bananas.html")
+	else:
+return render_page("some_other_template.html", options=options)
+

And, lo and behold, I can go to example.com/some-convoluted/url/bananas/with-stuff/ and see the right generated page! I like having that capacity at my fingertips. In particular, it makes it easy to respond to various other kinds of requests. That's what I really like about Flask - I feel like I can do everything a webserver can do, but it all takes the same very small amount of work. This compares to, say, PHP, where it's easy to make pages... But more complicated behavior, especially anything involving dynamic URL paths, involves jumping through hoops.It might seem like an overblown complaint, but the integrity of my mental models is important to me - and in PHP, I don't understand what gets sent to the requestor. If I make a Flask route returning JSON, I know it's exactly what a script wants. With PHP, I would hope that it wouldn't have weird headers or content type or additional markup. Plus, you have to work with PHP, which I would wish on no one.

+

I don't like Flask because I need to specify all the behaviors, and I'm worried that I'll get it wrong (thus, this article).

+

My ideal world solution would be something that lets me pass requests for subpaths to other python modules:

+

    +
  • server/ +
      +
    • server.py
    • +
    • main_page.html
    • +
    • articles/ +
        +
      • articles.py
      • +
      • auriga.html
      • +
      • language-for-games.html
      • +
      • tamari.html
      • +
      +
    • +
    • games/ +
        +
      • games.py
      • +
      • scores.csv
      • +
      • play_the_game.html
      • +
      • game.js
      • +
      +
    • +
    +
  • +
+

Aaaand how it works would be simple:

+
    +
  1. A request comes in to http://example.com/tamari/, and it is handled by server.py.
  2. +
  3. server.py says "Okay, I don't have any specific handlers that do anything with this URL, but I think articles/articles.py does!" and sends the request to articles.py.
  4. +
  5. articles.py receives a request for /tamari/ and has a rule for handling that, and so it renders the appropriate HTML file.
  6. +
+

The basic idea is that requests can be routed to other python modules. If those modules can be reloaded easily (or automatically!), this would mean I could write complicated URL handling in subdirectories of my website - without having to touch any of the code that handles anything else. It means I can modularize - and that's awesome.

+
    +
  1. An HTTP GET request comes in to http://example.com/games/points, and server.py takes it.
  2. +
  3. server.py doesn't know what to do with it, but it knows that requests for URLs starting with /games should be handled by games/games.py.
  4. +
  5. games.py receives an HTTP GET request for /games/points, and so it can respond to this with a JSON object that tells the requestor how many points they got.
  6. +
+

This is the main point of the tiered handlers system - that I can write handlers for all kinds of requests, without having to interact with other parts of my website.

+

Basically, I want to combine the simple directory structure of PHP websites with the principles of Flask I really like - separating content from server logic, keeping things simple, and having control.

+

Here's how I imagine this would work:

+
+from flask import Flask, render_template, redirect
+
+app = Flask(__name__)
+
+def main_page(): return render_template("main.html")
+app.add_url_rule("/", main_page)
+
+import games
+app.add_module_url_rule("/games/<path:rest>", games)
+

As usual, don't think too hard about the syntax I just proposed. The point is to distribute URL routing logic throughout multiple files, so that my entire website isn't controlled by one brutalistic overlord server.py file that decrees the entire path of every request. I want things split up, dammit!

+
+

Now I'm reading up on the Flask docs, and it turns out that it already has the capacity for this. Unfortunately, it's complicated and I'm whiiiny.

+

It seems that Flask provides the developer with three options:

+
    +
  • Package Organization: Essentially, break up your server code, like configuration and URL route assignment, into separate files. This doesn't actually solve any of my issues - most of what this does (as far as I can tell) is separate the server initialization logic from the routing and response logic. Plus, it makes it easier to use your application in something else. More on this soon.
  • +
  • Blueprints: Very close to what I want. I could define a Blueprint object in articles.py. This Blueprint object would register all the routes and endpoints that I want to register - and then, when the server starts, it gets all of those from the blueprint. This means we can actually factor views out. I'm really not sure, though, what this affords that the organizing your application as a package doesn't.
  • +
  • Application Dispatching: The most powerful of the three! This is a slightly lower-level solution, wherein you actually run multiple applications concurrently, and dispatch requests to them. This is kind of overkill, but at least it's nice to know that it's possible - things like the async_app example from previous could really make sense, if the app is large enough in scope. Application dispatching means you can just pull in another application and route things to it on the WSGI layer. Or something.
  • +
+

Okay, but... how do they score up? I have some requirements:

+
+
Organization
+
I want to define the behavior for example.com/articles/tamari/ in the same place as example.com/articles/language-for-games/ is defined, but in a different file from where example.com/games/hunt-the-wumpus is defined.
+
Simplicity
+
Defining a URL route and endpoint for each subfolder of my application shouldn't be much more complicated than defining behaviors in a flat system. Ideally, registering all the behaviors for the articles/ subpath is no more complicated than app.route("/articles/", articles_handler)
+
Ease of Extension
+
I'm writing a family of pages. They're starting to get a little too complicated. It should be really easy to go from that situation to having a nicely-delineated subsection of my website.
+
Do I Need to Restart the Server?
+
I hate restarting the server. Maybe this is a little thing, but gah! I want the server to detect changes automatically! This shouldn't be that hard. Of course, this is a much thornier topic when it comes to subpages that might store their own state... So it might be out the window unless I want to get my hands way more dirty.
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

How do they fare up?

OrganizationSimplicityEase of ExtensionNo Restart Required?
Current
Organize as a Package
Blueprints
Application Dispatch
+
+

In light of this chart, it seems like Blueprints are the clear option. Also, "organize as package" is really vague. What they suggested didn't sound very helpful - but making python packages (finally learning how to use __init__.py files!) will probably get used in the solution I'm thinking I'll use, which looks something like this:

+
    +
  • server/ +
      +
    • server.py
    • +
    • articles/ +
        +
      • article_routing_blueprint.py
      • +
      • main_page.html
      • +
      • auriga.html
      • +
      • language-for-games.html
      • +
      • tamari.html
      • +
      • tamari_files
      • +
      +
    • +
    • games/ +
        +
      • games_blueprint.py
      • +
      • scores.csv
      • +
      • play_the_game.html
      • +
      • game.js
      • +
      +
    • +
    • static/ +
        +
      • favicon.ico
      • +
      • stylesheet.css
      • +
      • lol_a_cat.jpg
      • +
      +
    • +
    +
  • +
+

If there's static content that is globally important, with simple routing rules, it can - of course - be stored in the server root's static/ directory. Static file handlers don't take much configuration, so this can go in the root.

+

All the files related to articles are contained in the articles/ directory, or subdirectories. article_routing_blueprint.py contains a blueprint definition that tells the server how to route calls that result in the rendering of the various articles I've written.

+

All files related to the little javascript games I've made, on the other hand, go in the games/ directory. State for this application is managed by games_blueprint.py, which is sort of like a very tiny application. I think it's possible to keep track of state, if you remember to keep variables global... This is tricksy, but should be doable - and if it gets larger, it should almost certainly be developed as a separate application entirely. At that point, I should figure out how to include multiple applications in one website!

+

I want to finish this bit by writing down some code for the base server.py logic:

+
+from flask import Flask
+
+# app configuration and initialization
+app = Flask(__name__)
+app.debug = True
+
+# content in articles/
+import articles
+app.register_blueprint(articles.blueprint)
+
+# content in games/
+import games
+app.register_blueprint(games.blueprint, url_prefix="/games")
+
+

Okay, one last thing.

+

This has been a lot about how to get some basic extensible functionality working. But wait, someone cries in the distance! Why are we going to such lengths to do all this in the first place, when you're just serving static HTML files? Isn't this way more work than you need?In case you're reading this article long after I've finished these changes (I hope so, 'cause I should get to work on this immediately), at this point all the articles were static HTML files. That voice speaks the truth.

+

Thanks for asking. The answer is that I want to change how this site is structured, and the articles are the specific thing I want to change. See, as it is, each article has basically the same header at the top, and basically the same footer at the bottom, and... Well, they're just all enclosed in the same stuff, and so when I change the styling of the website - or, horror of horrors, try to add a sidebar across the hwole site - I have to change all of them! And, as I think we've been over before, I'm lazy.

+

Conveniently, as I've been obsessively reading discussions of standards-compliant HTML5 and document formats and such lately, this laziness messes with my indomitable enthusiasm for conceptually pleasing solutions. Here's what I've got:

+
    +
  • Articles on my site are blocks of text, consisting of my rambling on some topic. Something I've written down. They can have some amount of associated content, but for the most part they're just text. So, I'm going to reduce them down to their bare minimum of unique content - in many cases, just an <article> tag containing what I wrote!
  • +
  • But sometimes I want to have multiple articles display in one page! Sometimes my articles are single paragraphs. Sometimes they can be grouped into related, uh, groups! What do we do then? The answer is templates. Flask uses Jinja2, a perfectly nice templating engine - and why not use it?
  • +
  • If I want to display the articles contained in articles/parkour_is_awesome.html, articles/exercise.html, and articles/how_is_body.html), then I just make a new page, a Jinja2 template, which contains this: +
    +{% include "articles/parkour_is_awesome.html" %}
    +{% include "articles/exercise.html" %}
    +{% include "articles/how_is_body.html" %}
    +
  • +
+

And ta-da! We now have a page which has those three articles in a row.

+

This, of course, also means we have a neat format for how to display things in general. Someone requested access to a URL! Find the file it points to. If it's an HTML fragment, then point the general page template to that file, and render. The general page template is just a template that adds the header, footer, sidebar, universal styling, and so on, as required, for universality.

+

So if you go to example.com/tamari/, the server says "Okay, you want to look at the article in the HTML fragment file articles/tamari.html." It renders the general page template around that article, and returns that to the user.

+

Because I like having the option to look at these things many ways, I'm probably also going to allow for a user to do python articles.py --generate [path], which will simply spit out the the rendered results of going to [path]. I guess that might just be indistinguishable from using wget, but hey. It can't hurt to give more functionality!

+

It remains to be seen precisely how many pages on this site will use the general page template. I don't want to rely on it too much, but, man, is there any reason not to? Basically, it means that I don't have to worry about writing the headers of anything on my website. I guess I'll need to be able to throw it off when I'm making subpages that are intended to be stylistically distinct from the rest of my site.

+
+ + diff --git a/pages/game_log.html b/static/backup/game_log.html similarity index 97% rename from pages/game_log.html rename to static/backup/game_log.html index d8f38ba..b9809a3 100644 --- a/pages/game_log.html +++ b/static/backup/game_log.html @@ -3,9 +3,9 @@ Game Log - - - + + +