Installing GHC 7 and cabal-install on Ubuntu 11.04

Since Ubuntu doesn’t package the latest version of GHC, I decided to install it manually and ran into some trouble. I couldn’t find any central repository for the steps needed to get both installed and working, so I’ll put them here. These were run with x86-64 Ubuntu 11.04.

  1. Add someone561’s PPA, which contains GHC 7:
    sudo add-apt-repository ppa:someone561/ppa
  2. Update and install GHC (this will uninstall any previous version of GHC you have):
    sudo apt-get update
    sudo apt-get install ghc

Now you have GHC 7, and the remaining step is to install cabal-install. I followed the steps from a Gist for Mac OS X:

  1. Grab the latest version of cabal-install from Hackage, and extract it somewhere. Change to that directory.
  2. Apply a patch to the cabal-install.cabalfile:
    curl -O http://hackage.haskell.org/trac/hackage/raw-attachment/ticket/872/ghc7.diff
    patch -p0 cabal-install.cabal ghc7.diff
  3. Modify bootstrap.shto use newer packages:
    1. Replace the version numbers/regexps with these (from the Gist):
      PARSEC_VER="3.1.1";    PARSEC_VER_REGEXP="[23]\."     # == 2.* || == 3.*
      NETWORK_VER="2.3.0.5"; NETWORK_VER_REGEXP="2\."       # == 2.*
      CABAL_VER="1.12.0";    CABAL_VER_REGEXP="1\.12\.[^0]" # == 1.10.* && >= 1.10.1
      TRANS_VER="0.2.2.0";   TRANS_VER_REGEXP="0\.2\."      # == 0.2.*
      MTL_VER="2.0.1.0";     MTL_VER_REGEXP="[12]\."        # == 1.* || == 2.*
      HTTP_VER="4000.1.2";   HTTP_VER_REGEXP="4000\.[01]\." # == 4000.0.* || 4000.1.*
      ZLIB_VER="0.5.3.1";    ZLIB_VER_REGEXP="0\.[45]\."    # == 0.4.* || ==0.5.*
      TIME_VER="1.2.0.4";    TIME_VER_REGEXP="1\.[12]\."    # == 0.1.* || ==0.2.*
      RANDOM_VER="1.0.0.3";  RANDOM_VER_REGEXP="1\."        # == 1.*
    2. Find the block of info_pkgcommands and add this one:
      info_pkg "random"       ${RANDOM_VER}  ${RANDOM_VER_REGEXP}
    3. Find the block of do_pkgcommands and add this one:
      do_pkg   "random"       ${RANDOM_VER}  ${RANDOM_VER_REGEXP}
    4. Comment out the info_pkg and do_pkg commands that deal with Cabal.
    5. If desired, change cabal to be installed globally:
      SCOPE_OF_INSTALLATION="--global"
    6. Install:
      sh bootstrap.sh

      (run with sudo for global installation scope)

Hopefully this helps anyone who runs into trouble!

Progress Update 2: Archon

I won’t post a transcript, but here’s the changes since my last post:

  • Loading and saving games
  • Battle system more complete
  • NPC chat (very basic)

Here’s an interesting statistic: the number of lines of code in Archon currently vs. the number in Sonata, my previous (unfinished) project:

Archon

--------------------------------------------------------------------
Language         files          blank        comment           code
--------------------------------------------------------------------
Python           16            439            310           1818

Sonata

--------------------------------------------------------------------
Language         files          blank        comment           code
--------------------------------------------------------------------
Python           47            778            891           2757

Although Sonata had to deal with rendering/graphics stuff, it was all that sort of stuff – there were no actual features. In contrast, Archon has implemented many features, and is still about a thousand lines of code less.

(Stereotypical, everyone-should-know-this, I-already-heard-it-too-many-times) lesson learned: don’t over-engineer.

Progress Update: Archon

Since my reflection, I’ve made some progress on the capabilities of Archon, the development name of my text-based game. (Ironically, it is named after the second failed game that I talk about in the reflection.) Here is a log showing what is currently possible:

Script started on Sun 26 Jun 2011 09:59:23 AM MST
Welcome to the demo.
Choose an option:
[0]: New Game
[1]: Load Game
[2]: Exit
> 0
Wed, Jan 01 12:00> use customizer
Welcome to the customization routine.
Name? David Li
What's your gender? Male
Describe yourself briefly: No.
Your acumen in the three traits, Physical, Mental, and
Spiritual, is determined randomly at the beginning. The values will be
generated now. Enter [y/yes] to accept them, or [n/no] to decline and try
again. Only three tries are allowed.

Try #1
mental: 38
  absorb: 0.61 to 0.75 multiplier
  drain: 0.42 to 0.51 multiplier
  evade: 0.61 to 0.75 multiplier
  fatigue: 0.42 to 0.51 multiplier
  success: 0.61 to 0.75 multiplier
physical: 20
  absorb: 0.54 to 0.66 multiplier
  drain: 0.6 to 0.74 multiplier
  evade: 0.54 to 0.66 multiplier
  fatigue: 0.6 to 0.74 multiplier
  success: 0.54 to 0.66 multiplier
spiritual: 10
  absorb: 0.49 to 0.6 multiplier
  drain: 0.74 to 0.9 multiplier
  evade: 0.49 to 0.6 multiplier
  fatigue: 0.74 to 0.9 multiplier
  success: 0.49 to 0.6 multiplier
Accept?  [y, yes] [n, no] y
Welcome, David Li, to the demo. Try `go on` to continue.
Wed, Jan 01 12:20> describe
You are in an area to start a new game.
You can go on.
Wed, Jan 01 12:20> go on
You are in a test room.
There is a door to the north.
There is a thing in the south.
You can go south.
Wed, Jan 01 12:20> go south
You are in a test room.
There is a cave on the side of a cliff. It is quite interesting, but looks dangerous.
There is an iron sword. It is something you should `take`.
There is a thing.
You can go north.
Wed, Jan 01 12:20> describe thing
A test object.
Wed, Jan 01 12:20> take iron sword
Wed, Jan 01 12:20> equip 0
Wed, Jan 01 12:20> inventory
Inventory (1)
0. Iron Sword
Wed, Jan 01 12:20> equip
body: <Empty>
feet: <Empty>
head: <Empty>
left hand: Iron Sword
legs: <Empty>
neck: <Empty>
right hand: <Empty>
shoulders: <Empty>
Wed, Jan 01 12:20> describe
You are in a test room.
There is a cave on the side of a cliff. It is quite interesting, but looks dangerous.
There is a thing.
You can go north.
Wed, Jan 01 12:20> enter cave
Go to Dark Cave: Entrance?  [y, yes] [n, no] y
A dark cave, full of danger and enemies.
You are in the beginning of a dark cave.
There is an imp in front of you. It is perhaps something you could defeat.
There is another imp blocking the route to the rest of the cave.
You can go out.
Wed, Jan 01 12:20> fight an imp
Turn 0 HP 21.0 AP 10.0> attack imp
You hit the enemy for 1.7 with Iron Sword.
Effect(hit=True, target=EffectTarget(category='vital', kind=None, target='ap'), magnitude=-0.3333333333333333, turns=-1, drain=0)
The enemy does nothing.
Imp (Tester): 0.0 HP
Effect(hit=True, target=EffectTarget(category='vital', kind=None, target='ap'), magnitude=-0.3333333333333333, turns=-1, drain=0)
Turn 1 HP 21.0 AP 9.4> attack imp
You hit the enemy for 0.5 with Iron Sword.
Effect(hit=True, target=EffectTarget(category='vital', kind=None, target='ap'), magnitude=-0.3333333333333333, turns=-1, drain=0)
The enemy does nothing.
Imp (Tester): 0.0 HP
Effect(hit=True, target=EffectTarget(category='vital', kind=None, target='ap'), magnitude=-0.3333333333333333, turns=-1, drain=0)
Turn 2 HP 21.0 AP 8.6> attack imp
You hit the enemy for 3.0 with Iron Sword.
Effect(hit=True, target=EffectTarget(category='vital', kind=None, target='ap'), magnitude=-0.3333333333333333, turns=-1, drain=0)
The enemy does nothing.
Imp (Tester): 0.0 HP
Effect(hit=True, target=EffectTarget(category='vital', kind=None, target='ap'), magnitude=-0.3333333333333333, turns=-1, drain=0)
Turn 3 HP 21.0 AP 7.8> ac ttack imp
You hit the enemy for 2.9 with Iron Sword.
Effect(hit=True, target=EffectTarget(category='vital', kind=None, target='ap'), magnitude=-0.3333333333333333, turns=-1, drain=0)
The enemy does nothing.
Imp (Tester): 0.0 HP
Effect(hit=True, target=EffectTarget(category='vital', kind=None, target='ap'), magnitude=-0.3333333333333333, turns=-1, drain=0)
Turn 4 HP 21.0 AP 7.1> quit

Script done on Sun 26 Jun 2011 10:00:19 AM MST

Features:

  • Character stats
  • Character creation
  • Inventory
  • Equipping items
  • (Incomplete) battle system

The project is already much more complete than the previous two projects, but there’s still a lot more to do:

  • Save games
  • Documentation
  • Battle system
  • NPC interaction (shops, dialogue, quests, etc.)
  • Content!

However, this time around, I’ll actually get around to those features.

Reflection: Coming Full Circle, Twice

Every once in a while, I stop and try to measure my progress in programming since starting in 6th grade, and recently, I noticed that this is my third time making a text-based video game in as many years. Why do I continue to return to this idea? The first two times, I believed that it would be a simple project and a learning experience – yet each time, I made basic beginner mistakes.

The first time, C# was my language of choice – I was even more of a beginner then than I am now. Mistakes? I believed subclassing formed namespaces (however that idea entered my mind, I would like to find out), i.e. I barely understood my language. Design existed – I still have a notebook filled with plans – but it was useless and misguided: I planned for separate “SpecialItem”, “QuestSpecialItem”, “ToolSpecialItem”, and “IngredientSpecialItem” classes, for a start. Basically, I was an idiot who couldn’t program. And what code I did write was a failure:

public Item(int uniqID, string file){
    this.id = (int) uniqId;
    string temp = TKIO.load(file);
    // other stuff
    return (id);
}

And in my notes, I noted that this was a function to create an item. Perhaps I should just stop programming now.

Thankfully, I later abandoned that enterprise, but a few years later, I started to create another game – this time, with more experience under my belt, and with Python rather than C#. Now, while the first attempt had been a massive case of ignorance with regards to programming, this one was completely over-engineered –  it completely missed the mark again. I had code to update the database (which never really contained anything) with a UI written with HTML and JavaScript served by a Python webserver. Everything was based around  a complicated plugin system where everything would happen based on events, which could be useful if it didn’t add massively useless indirection to everything. For instance, this is how the “CharacterPlugin”, the only one I ever actually got around to writing, initialized itself:

self.client_system.register_handler('mainloop_enter',self.character_creation)
self.client_system.register_handler('command:character_info',self.character_info)
self.client_system.register_handler('command:skill_list',self.list_skills)
self.client_system.register_property('character:skill_info',self.get_skill)
self.client_system.register_property('character:character',self.get_char)
self.client_system.register_property('skill:skill_info',self.get_skill_data)

I remember spending hours debugging the event system that made all this (somehow) work… Yet I didn’t realize that I was over-engineering everything. Here’s a quote from the (8 lines) of design documents I had, describing my goals for the game code:

Not reusable; it will do what I want. Period.

Right.

So, what happened in the interim? Why do I think I’m a better programmer this time?

Well, I’ve made more progress, for one:


Welcome to the demo.
Choose an option:
[0]: New Game
[1]: Load Game
[2]: Exit
> 0
> describe
You are in an area to start a new game.
There is a customizer. It is something you should `use` right now.
You can go on.
> go on
You are in a test room.
There is a door to the north.
There is yet another thing in the northwest corner. It is interesting.
There is a thing in the south.
You can go south.
> enter door
Go to Test Room 2?yes
You are in a test room.
There is a thing.
You can go north.
> go north
You are in a test room.
There is a door to the north.
There is yet another thing in the northwest corner. It is interesting.
There is a thing in the south.
You can go south.
> quit

Ignoring the crappy content, there’s more achieved – an actual game, where you can walk around a bit. (I haven’t implemented commands for the character yet.) The total LOC count, according to cloc, is 641 for my current project – and 895 for the previous one. That’s one form of progress. And I believe my code is not over-engineered this time around (as much): a simple game can be set up with

ds = archon.datastore.GameDatastore(
    'demo/data', archon.datastore.LazyCacheDatastore)
save = archon.datastore.GameDatastore(
    'demo/save', archon.datastore.LazyCacheDatastore)
room = ds['newGame.newGame']
player = save['player_template']
interface = archon.interface.ConsoleInterface()
interface.repl(room, player, archon.commands.command)

Adding a new command is simple:

@command('go')
def go(output, context, player, *args):
    '''Go in the specified direction.'''
    direction = args[0]
    target = context.outputs.get(direction)
    if target:
        context.exit()
        target.enter()
        return command.get('describe')(output, target, player)
    else:
        output.error("You can't go that way.")
        return context

There’s abstraction – the use of output to abstract the actual output method – but also evidence of more learning: instead of modifying globals (or references to them), which is what self.client_system really was in the code for my previous project, it uses a more functional approach – the output, context (current room/location), player, and arguments are given to the function; if the room is changed, return the new one, else return the old one back.

So, even though I’ve come full circle, twice, I think each time, I’ve branched out more from my beginnings – more like a spiral.

Why I’m an Initiate Coder: Overambition

I’ve tried to avoid admitting it to myself before, but I must now face a basic mistake – no, two – that I’ve made with regards to game development:

  1. Making an engine, not a game. ScientificNinja’s article was my wake-up call: Sonata, while not really an engine, is a library – but not a game.
  2. Making a game that is too complex. Final Fantasy I may be simple and boring by today’s standards, but to a novice in game development and an inexperienced programmer, it is immensely more complicated than it may appear.

These two problems make themselves pretty evident. After nearly a year of development (though with a 4 month hiatus for Science Fair), I am no further than last summer – perhaps I have a better-looking grass tile and some cleaner code, but the demo still doesn’t do anything except let you walk around and run into some water. Meanwhile, my “design documents” are full of holes, and I’ve spent a lot of time engineering a resource system. Therefore, I will scale myself back and build something simpler – perhaps Pong, Pac-Man, or another little game, and then work my way up. There’s a good GameDev.se topic on this – “What are good games to “earn your wings” with?” that will be useful.

Anyways, Sonata is probably going to stagnate for a really long time.

Reinventing the Wheel

Well, I just posted that I was basically reinventing the wheel with regards to sprites in Pyglet. Yet, after a few minutes closer inspection of what I need accomplished and what can be done with the built-in classes, now I realize that my original decision to implement my own “actors” was correct – Pyglet’s sprites don’t really provide what I want or need in an application. For instance, I need separate animations for directions of movement, as well as static images for when a character is not moving. The built-in classes don’t allow me to change an image, while an animation can’t be easily paused, either, since a Pyglet Animation simply represents the necessary data – it doesn’t render itself, instead delegating that power to other classes over which I have no control. However, I still believe that I have over-engineered my resource system; that was a waste of time.

Addendum: Still Overengineering a Resource System

I realized that I don’t need such a resource system as I’ve described previously. Instead, I just need something that can transform a hierarchical directory tree into something that I can easily traverse in my program, and something that can load default properties and instantiate things like built-in Pyglet objects rather than all the custom crap that I’ve written. For instance, the built-in Sprites already support batching, animation, and so much more. They can’t be serialized or loaded, but I don’t need the former, and the latter can be achieved by simply subclassing. And their batching support makes them perfect for implementing a tilemap – they’re easy to manipulate and support arbitrary animation, yet can be rendered in a single call. There’s already been discussion on how to efficiently render a tilemap, too. No support for collision is built in, but that should be delegated to an external object anyways.

Therefore, all my resource system needs to do is:

  • Instantiate the appropriate object when needed.

A JSON file containing data such as image used is all that is needed.