Tabbed list to HTML in CoffeeScript

A friend asked for a script to converted a tabbed list of data to a nested HTML list. Here it is: http://codepen.io/jamiely/pen/AbEjd. Before working on this, I didn’t know CodePen had live CoffeeScript editing. That feature moves it into the favorite spot above jsfiddle for live code editing.

Given a sample of text like this:

Dog
	Puppy 1
	Puppy 2
		Page 1
		Page 2
	Puppy 3
		Page 1
		Page 2
		Page 3
		Page 4
	Puppy 4
Cat
	Kitty 1
	Kitty 2
		Page 1
			Paragraph 1
			Paragraph 2
		Page 2
	Kitty 3
Turtle
Horse
	Pony 1
	Pony 2
	Pony 3

Convert it to an equivalent HTML list like this:

<ul>
	<li>Dog</li>
	<ul>
		<li> Puppy 1</li>
		<li> Puppy 2</li>
		<ul>
			<li> Page 1</li>
			<li> Page 2</li>
		</ul>
		<li> Puppy 3</li>
		<ul>
			<li> Page 1</li>
			<li> Page 2</li>
			<li> Page 3</li>
			<li> Page 4</li>
		</ul>
		<li> Puppy 4</li>
	</ul>
	<li>Cat</li>
	<ul>
		<li> Kitty 1</li>
		<li> Kitty 2</li>
		<ul>
			<li> Page 1</li>
			<ul>
				<li> Paragraph 1</li>
				<li> Paragraph 2</li>
			</ul>
			<li> Page 2</li>
		</ul>
		<li> Kitty 3</li>
	</ul>
	<li>Turtle</li>
	<li>Horse</li>
	<ul>
		<li> Pony 1</li>
		<li> Pony 2</li>
		<li> Pony 3</li>
	</ul>
</ul>

And here’s the relevant bit of CoffeeScript to do it:


# Call `convert`!
# Converts tabbed-text to HTML
convert = (text) ->
  parse text.split '\n'

# Creates a list item element from a piece of text
li = (t) ->
  html = "<li>#{t['line']}</li>"
  ptAccum.push html
  html

# Creates a start UL tag
ul = (t) ->
  ptAccum.push "<ul>#{li(t)}"
 
# Creates an ending UL tag
ulEnd = ->
  ptAccum.push "</ul>"
  
  
# Will be used to store the generated HTML
ptAccum = []  
# Will be used to track progress
index = 0

# Begins the parsing procedure
parse = (lines) ->
  ts = linesToMaps lines
  ptAccum = ["<ul>"]
  index = 0
  parseTuples ts, 0
  ulEnd()
  ptAccum.join "\n"

# Does the bulk of the parsing job, keeping track of the indentation level
parseTuples = (tuples, level) ->
  stop = false
  _p = ->
    t = tuples[index]
    curLevel = t['level']
    index++
    if curLevel == level
      # sibling, process the current at the same level
      li(t)
    else if curLevel < level
      # we want to unindent
      # dont do anything here
      index--
      stop = true
    else
      # we are at the first child
      ul(t)
      parseTuples tuples, level + 1
      ulEnd()
      
  _p() while !stop && index < tuples.length

# Returns the number of tabs that prefix a line
tabCount = (line) ->
  tc = 0
  c = '\t'
  count = 0
  count = line.length if line
  i = 0
  
  isTab = ->
    c == '\t'
  
  inc = ->
    c = line.charAt(i)
    tc++ if isTab()
    i++
  
  inc() while isTab() && i < count
    
  tc

# Converts the passed line to a map containing the line and meta-data about the line
lineToMap = (line) -> 
  line: line
  level: tabCount line

# Returns true if the string is blank
blank = (line) ->
  !line || line.length == 0 || line.match /^ *$/

# Converts the passed lines into maps rperesenting the line
linesToMaps = (lines) ->
  lineToMap line for line in lines when !(blank line)

Setting up a MonoGame Mac Application with Xamarin Studio

Intro

I wanted to try out the MonoGame framework, which allows for creating cross-platform games based on the XNA API. It wasn’t straightforward for me to run a Mac example (on OS X Mountain Lion). There were a number of steps I had to go through to get things working, including combing through forum posts. The official instructions didn’t seem to work, or I couldn’t find the most recent update. It’s for situations like these I wish I had the foresight to journal my progress so that others don’t get tripped up. Here are instructions I followed (assembled after the fact, so I might’ve missed something) in case someone finds them useful.

Steps

  1. Install Xamarin Studio and the specified dependencies (mono, gtk+).
  2. Install the MonoGame project template for Xamarin Studio
  3. Install XCode from the Mac App Store.
  4. Open Xamarin Studio and perform updates.
  5. Create a new MonoGame Mac Project.
  6. In References, you may see MonoGame.Framework. As of 20130422, this is the incorrect framework. Command click on Refrences > Edit References > .Net Assembly > Navigate to ~/Library/Application Support/XamarinStudio-4.0/LocalInstall/Addins/MonoDevelop.MonoGame.3.0.1/assemblies/MacOS/ and select MonoGame.Framework.dll > Press OK. Delete the other reference to MonoGame.Framework.
  7. Running the project, you may get an error like: “Microsoft.Xna.Framework.Content.ContentLoadException: Could not load logo asset”. Select Content > logo.png in the file pane at the left. Click the Gear Icon > Build Action > Select BundleResource.
  8. Run the project again. You may get an error like “System.MissingMethodException: Method not found: ‘MonoMac.AppKit.NSImage.AsCGImage’”. This forum post helped me to resolve it..
  9. Clone maccore to your code directory git://github.com/mono/maccore.git
  10. Clone monomac to your code directory git://github.com/mono/monomac.git
  11. Run make inside the monomac project.
  12. If you get an error about missing mdtool, you can either install an old version of MonoDevelop (< 4) from SourceForge or try to symlink mdtool to the location requested from /Applications/Xamarin Studio.app/Contents/MacOS/mdtool
  13. Find one of the MonoMac.dll binaries from the samples directories, for example, at samples/MonoMacGameWindow/bin/Debug/MonoMac.dll, copy it somewhere you can use for your project.
  14. Add the MonoMac.dll assembly to your project as a Reference, like we did for MonoGame.Framework.dll, above. Delete the other reference to MonoMac.
  15. Now, when you run the project, a window with a blue background should appear, displaying the MonoGame logo.

Tetris Attack ClojureScript Update – Performance Update

Intro

See my previous posts about a Tetris Attack implementation in ClojureScript. The game (as of tag v1.0.3) runs decently on my MacBook Air running Chrome 26. In FireFox 18, the game kind of crawls. Let’s try to figure out the problem. I’ll be looking at project tag v1.0.3.

Open Chrome DevTools. Navigate to the “Profiles” tab. Refresh the app and select “Collect JavaScript CPU Profile” and click the “Start” button to begin profiling. Use the app for awhile and when you’ve done some representative tasks, click the red record button (or the “Stop” button) to stop profiling.

Select “Profile 1″ and sort by Total %, making sure “Tree (Top Down)” is selected as the view type. I prefer to use this view so that I can drill down the call stack. It’s also easier to understand when you have very functional code. For example, if I view the profile in “Heavy (Bottom Up)” mode, then reduce is shown as taking up a large percentage of time.

So far we use a very trivial algorithm to detect falling blocks. Profiling shows that the code that detects falling blocks is a bottle-neck and so must be improved.

Take a look at the image which shows that 70% of time is spent in the resolve-grid function called from step (our game loop function). Drilling into resolve-grid, we see that most of the time is spent in create-falling-blocks, and a function it calls should-block-fall?. This method starts a chain of calls that builds a list of occupied spaces each time it’s invoked.

profiling_v1.0.3

Let’s come up with a more efficient algorithm for detecting falling blocks.

Falling

So what does it mean for a block to be falling? A block is falling if there is no block beneath it in the grid.

One might create a grid representing the data structure and fill this in, iterating from the bottom up. Another way to do this is to define a block falling recursively.

Recursively

A simple recursive algorithm for knowing if a block is falling in clojure-esque pseudocode

(defn falling? [block]
  (if (in-bottom-row? block)
    false
    (if (nil? (block-below block))
      true
      (falling? (block-below block)))))

Simplification

This algorithm does not account for garbage blocks, which have special considerations in both falling and determining what the block-below is.

Another simplification is that the grid must be passed in to perform the lookups for blocks that are below the current block.

Instead of passing the grid in though, we may want to consider passing in blocks as linked-lists of columns by row. This would fit with the algorithm above. If we pass in lists of columns then we can also reduce the number of checks needed for block-below.

Memoization

One other improvement in performance is to memoize the falling values as we call falling?. There is a memoize function built into Clojure. The trick is that we want the function to be memoized but recursive. This is a good StackOverflow question that explains how to create a memoized recursive function. The first answer seems like what we need, but refs don’t seem to be supported in clojurescript. Instead, we write our method using the style in the second answer, which requires binding the var to the namespace.

Rewriting the example above, we get something like:

(def falling?
  (memoize (fn [block]
             (if (in-bottom-row? block)
               false
               (if (nil? (block-below block))
                 true
                 (falling? (block-below block)))))))

Not only is the definition of falling more clearly and elegantly stated, but the memoization should result in greater efficiency.

Results

Rewriting the create-falling-blocks function to use what we’ve talked about, yields the following results upon profiling.

profiling_v1.0.4

We have increased idle % from around 3% in tag v1.0.3 to around 20% in tag v1.0.4, and lowered % time in create-falling-blocks by around that amount.

Anecdotally, v1.0.4 seems smoother than v1.0.3 in Firefox and has better Chrome-performance as well.

Book Notes – The Swerve: How the World Became Modern (Chapters 6-9)

Chapter 6: In the Lie Factory

  • Rome had powerful families
  • Poggio wanted to be apostolic scriptor
  • Pope ruled many spiritually but had unstable physical kingdom
  • Lots of cases in the papal court (the Roman Curia)
  • He didn’t want to join the Church for fear of moral bankruptcy
  • Roman Curia was perilous
  • Lapo’s On the Excellence and Dignity of the Roman Court
    • Commentary on bribery and corruption
    • Wanted position in Curia
  • Poggio was papal secretary in 1430s
  • Poggio called the Cura the Lie Factory
    • Lots of slander, gossip, backstabbing
  • He wrote a successful work called Facetiae
    • A collection of stories and jokes
  • Intense compeition and backbiting
    • Said all kinds of shit about each other, attacking skills, family, hygiene
  • Poggio hated the way things were. He wrote other works about the subject.
  • He wanted to change his life
    • Some escapism provided by book-mania

Chapter 7: A Pit to Catch Foxes

  • Poggio worked under Baldassare Cossa, Pope John XXIII.
    • From a family of pirates.
    • Very skilled writer and politician, cunning
    • Suspected of poisoning previous Pope
    • Made money by throwing jubilees
  • Papal throne contested by two others
    • How to reconcile?
    • Way of Council, Constance, Germany
    • Christians from all over came
  • Jan Hus humiliated and imprisoned
  • Jerome of Prague put on trial months later
    • Poggio questions his crimes (heresy)
  • Cossa flees when things start to turn (he’s the fox)
  • Poggio decides to stay around and go book hunting
  • Without stability in his life, Poggio throws himself more deeply into humanities
  • He likened saving books with saving people, anthropomorphized books

Chapter 8: The Way Things Are

  • You might call Lucretius’ idea atheist, but he may have believed in gods
    • If gods exist, it doesn’t matter
  • Ideas
    • Atoms
    • Eternal atoms
    • Infinite atoms of set shape and size
    • Particles in motion in infinite void
    • Creation comes as the result of a swerve
    • Swerve is source of free will
    • Universe not created for humans
    • Humans not unique
    • Human society began as primitive battle for survival
    • The soul dies
    • There is no afterlife
    • Death is nothing
    • All organized religions are superstitious delusions
    • Religions are invariable curled
    • No agen;s, demons, ghosts
    • The highest goal of human life is the enhancement of pleasure and the reduction of pain
    • The greatest obstacle to pleasure is not pain; it is delusion
    • Understanding hte nature of things generates deep wonder
  • The work is also very poetic
  • Used Venus metaphore

Chapter 9: The Return

  • Poggio waited years to get access to Niccolo’s copy of On the Nature of Things.
  • When the new pope was installed, many of his colleagues returned to Curia
  • Poggio went to work for the Bishop of Winchester.
  • He didn’t find any great works in the English monasteries.
  • He returned in 1422 to a new secretarial post.
  • He resumed his work in the Curia
  • Had many children with Lucia Pannelli his mistress
  • Accumulated money and collected antiquities
  • He married into the Buondelmonti familly, Vaggia di Gino Buondelmonti. “Should an Old man Marry?”
  • He worked for several other popes, 8 in total.
  • Served as chancelor of Florence for 5 years. Resigned and died 18 months later in 1459.

Book Notes – The Swerve: How the World Became Modern (Chapters 1 – 5)

Notes for the book The Swerve: How the World Became Modern by Stephen Greenblatt.

Preface

Greenblatt picked up Lucretius’ On the Nature of Things in a college book sale. He was struck by the ideas it espoused, including ideas on how to handle death. He had been fearful of death through his mother, who used it as a manipulative device. He was intrigued that a book written so long ago could contain ideas we might consider modern.

Chapter 1: The Book Hunter

Poggio was the papal secretary, responsible for Latin correspondence. When Pope John XXIII gets deposed, he finds himself in Germany without a master. He goes looking for old manuscripts in monasteries. Hopefully they’ve been copied faithfully throughout the centuries–he’s hoping to find the words of ancient Rome.

Chapter 2: The Moment of Discovery

Certain rules of monastic orders accidentally led to their central role as archivists for ancient texts. These rules included requirements for literacy, the demand for books, and the spiritual importance of the monotonous work of transcription. Unfortunately, scribes would sometimes write over older texts to transcribe newer texts, at the direction of the abbey librarian. Being a book hunter during the time wasn’t easy. Although the priest Petrarch had made some amazing discoveries, Germany and Switzerland were had remote monasteries that might yield greater treasures. Poggio was uniquely suited to reclaim these ancient texts since he was knowledgeable about the church, a gifted Latinist, extremely charming, and a talented scribe as well. Greenblatt hypothesizes that Poggio made his way to and charmed his way into the library of the Benedictine Abbey of Fulda. There he would’ve found some interesting works, the most important of which was Lucretius’s On the Nature of Things.

Chapter 3: In Search of Lucretius

In Poggio’s time, Lucretius’ works were not well known (On the Nature of Things) is the only one to survive to modern times. What they knew was from small references by his contemporaries including Cicero, Ovid, and Virgil. The discovery of Herculaneum, buried by Vesuvius with Pompei, provided information about philosophical thought during that time. A small private library was unearthed that contained intact papyrii.

As the Romans expanded their empire, they came to admire Greek art, science, and philosophy. The rich and powerful used books as a status symbol, and some amassed great libraries.

Philosophical discourse was a social affair. Cicero, in writing about his philosophical ideas, made it a point to discuss the ideas of his colleagues, ideas that would’ve been discussed together during gatherings. Greenblatt supposes that Lucretius may have been at such a gathering at the library at Herculaneum.
Lucretius was inspired by Epicurius and his ideas of atoms and pleasure. Although it is commonly believed that Epicuirius’ idea of pleasure equates to that of the tantalization of the senses, evidence suggests he led a simple, wholesome life. Documents from the library discuss that pleasure in life comes not from indulgence, but a life of courage, honor, duty, virtue, and temperance. The Epicurean idea of atoms and the void provided an alternative to religious worship. Epicurus taught that if a person was able to keep these ideas in his mind, he would have no fear of the afterlife, and many of the pressures and stresses of life would fall away.

Chapter 4: The Teeth of Time

Only scraps of ancient learning have survived to the present day. The majority of these are copies of original texts; the only originals left have been found at a couple archaeological sites including Herculaneum. Papyrus written on with a water-soluble ink was the popular medium, susceptible to bookworms, storms and floods, and tears.

There was a thriving book industry in the ancient world. Copyists and scribes were actively used to produce books, and there were book shops and public libraries in major Roman imperial cities, including the most famous library in history, the Library of Alexandria, which was more like a university than a modern library.

The book culture thrived until the rise of Christendom after Constantine made it the official religion. Greenblatt presents the fate of Hypatia, celebrated philosopher of Alexandria, murdered by a Christian mob, as the beginning of the end of intellectualism and the truce between paganism and Christianity.

Notable early Christian thought-leaders talked about the conflict between classical pagan learning and leading a life in God’s grace.

Although the teachings of some of the Greek philosophers could be integrated into the Church’s official teachings, Epicurus’ teaching that there was nothing after death, and that life’s goal should be the pursuit of pleasure could not be reconciled with Church canon.

To combat Epicureanism, some Christian leaders painted Epicurus and his followers as hedonists. The Church also began to teach that the way to grace was through pain, that life was about pain and not pleasure. Some saints and monastic orders were regular practitioners of self-flagellation.

Chapter 5: Birth and Rebirth

Poggio arrived in Florence penniless despite growing up with some wealth; he had all the talents to succeed in Florence despite this. He had excellent penmanship, which he used to pay for lessons to improve his already proficient latin. He and some of his contemporaries were instrumental in the design of the type known as roman.

Petrarch and his contemporaries popularized the revival of classical pagan literature. While some believed that their civilization was a continuation of the great civilizations that came before, those in Petrarch’s camp believed that the empire was long dead and only shadows and fragments of it existed. One of Petrarch’s closest students was Salutati.

Poggio eventually became a mentee of Salutati, the Chancellor of Florence. He believed that Florence was the heir to ancient Rome, being an independent city state run by an elected council. Salutati was a brilliant politician who mentored not only Poggio but Leonardo Bruni, who would become important in Florence later, and Niccolo Niccoli, who had a great collection of antiquities and documents which he would bequeath to start a public library.

Lacking wealth or political ambition, Poggio decided to head to Rome to find his calling.


Tetris Attack ClojureScript Update – Garbage Blocks

In this v1.0.3 update to Tetris Attack Clojurescript, I implemented Garbage Blocks. In Tetris Attack, Garbage Blocks are dropped onto your playing field when your opponent makes a match. The dimensions of the block depend on the nature of your opponent’s match, whether it was a combo or a chain match.

For now, I’ve implemented randomly falling garbage blocks, which fall every 100 clock ticks. This seems somewhat fast in practice, and I’ll probably dial it down at some point, especially if I ever get around to adding the concept of levels to the game.

The clear a garbage block, you must make an adjacent match. Once that happens, the garbage block splits into a set of regular blocks. Currently, the garbage block will be completely dissolved into regular blocks, which is a different behavior than the original Tetris Attack game.

There are a couple of other improvements to performance as well, but it still runs best in Google Chrome. Below is a link to a screencast of the current version. You can also try the most current version here.

Jessie

When Jessie started worked for Mr. Humphrey, she had no idea how she’d come to feel about him. In the mornings, while she prepared breakfast for the family, he’d stomp down the stairs bracing the laughing Robin upon his shoulders. How grateful My. Humphrey had always seemed for the meal, as if it wasn’t Jessie’s job. The timbre of small gratitudes ushered in his deep, smooth voice brought a flush to her face.

It was a brisk Tuesday morning. After she’d seen Robin off at the bus stop, despite many chores waiting for her at the Humphrey home, she started walking towards the office where Mr. Humphrey worked. She had no specific aim, just hazy thoughts about her employer and their relationship. When she got to the office, she saw him through the large plate glass windows, and gazed at him for a while hidden behind a newstand.

The next day, before Robin had awoken, Mr. Humphrey came down the steps softly, meeting Jessie in the kitchen. “Jessie, can I speak to you for a moment about something important?” He said it softly and with a firmness that made tightened Jessie’s heart. “Of course, what is it?” she worried. “Did you go anywhere yesterday before I got home? Were you home all day?” Mr. Humphrey questioned.

Jessie’s hands started to sweat, and a different kind of flush conquered her face. “Yes, of course! I had so many chores to do, after I left Robin at the bus stop, I came right back here.” She hoped that he would not ask her if she had been around his office.

Mr. Humphrey brows furrowed, and his patient smile drooped at the corners. “Jessie, I have a daily morning ritual. Before I leave for the day, I open my late wife’s jewelry box, and I hold her favorite pair of diamond earrings for a moment, while I think of the last time I saw her wear them. It gives me some small comfort–enough to get through the day without her.” Jessie’s brows raised and her lips opened as if to say something, but she didn’t have anything to say. Mr. Humphrey’s voice now became hard, and his eyes pierced her, “This morning, I opened the jewelry box, and the earrings were gone.”

Written as part of the Hourly Story Seeds project (https://twitter.com/stiththompson). The motif was “Evading a direct answer which may trap one.” (https://twitter.com/StithThompson/status/305497286551670784)

ClojureScript – Tetris Attack

Intro

Having completed a simple implementation of the game Breakout in Clojure, I decided my next Clojure project should be doing something with ClojureScript. Not only would it provide an opportunity to continue Clojure practice, but I’d be able to leverage existing knowledge I have about JavaScript (probably the language I am most familiar with), and I could possibly use it in the future for a web project. Note that I’ve only tested this with the most recent version of Chrome. Performance is not great right now, but can be much improved. Here’s a demo.

Clojure v ClojureScript

I found the process of building projects, sharing code between Clojure and Clojurescript, and building the source to be very straightforward. I used cljsbuild to accomplish many of these needs. Especially useful was the repl which can be used to communicate with ClojureScript code running in the browser.

There were two sources of headaches I had with working with ClojureScript, as compared to Clojure.

  1. I had to create compatability namespaces to wrap thigngs that are available in clojure.core but not cljs.core
  2. I found a couple things (hex-string support) that were supported in clojure.core and not cljs.core, but were not documented.
  3. When cross-compiling Clojure code for use in ClojureScript, I did not see in the documentation how to make namespace functions exportable. Admittedly, I might find my answer easily if I were to ask on the mailing list. This means I haven’t gotten advanced compiling working yet, and the compiled script file is large at 1MB.

The Game

I decided to work on a clone of the game Tetris Attack, which has provided me with hundreds of hours of entertainment. The object of the game is to match 3 or more blocks. The complexity of the game comes in setting off chain reactions of matches.

Methodology

The game is represented as a simple HashMap of keys and properties, that looks something like the following:

  {:grid {:blocks [{:type :#FF0000 :position [1 1]}
                   {:type :falling :block {...} :falling-to [3 3] :ticks 15}
                   {:type :swap :blocks [...] :ticks 10}
                   ...]
          :rows 10
          :cols 6
         }
   :clock 10}

Blocks are determined by type. The simplest blocks store their color as their type. Other types have specific meanings. For example, a “falling” block is one which is falling down a grid column (as if pulled by gravity).

Some of these blocks have a property called :ticks, which determines the clock ticks left for the action to complete. For example, a falling block with 10 ticks left, will take 10 clock ticks to finish falling, at which point it may be resolved into a normal block.

In this way, the entire game-state is stored, and separate interface functions can work to alter the game state.

In terms of mutability, a single atom in the entrypoint namespace stores the entire game state and interface state. The rendering code is entirely separate and may operate on arbitrary game states.

Rendering

The rendering code is in the display namespace in a ClojureScript-specific file that performs drawing on an HTML canvas object. This namespace, along with the entrypoint namespace provide the specific code necessary to drive the web interface. Although ideally there’d be a namespace that contains only functions requiring implementation to support different platforms, display and entrypoint are not at that point. Still, implementing a version with an interface using three.js or using Java Swing should be fairly trivial.

Learning

Most of the projects I do are for learning rather than anything practical. Here are some of the main things I’ve learned about working on this project:

  • Some new Clojure techniques, such as using partial and into, the threading operator, testing with clojure.test, and some more about how namespacing works.
  • How to work with ClojureScript – This involves syntax, building, debugging, crossovers, interop, and testing.
  • How to implement a game almost entirely using immutable structures.

Media

You can find updated links to media as well as the full source in the project README. Here are (current as of the post date) screenshots and screencasts. Here’s a demo of the project. Please note that the script file is fairly hefty at (1 MB) since I haven’t gotten advanced compiling working yet.

8c8eb47

0b1d1da-gameover

Updating an old Flash project

In 2004, I wrote an implementation of Tetris in ActionScript 2 using Flash MX. I had thought the source code lost until just a few months ago. Upon finding that source, I immediately uploaded it to Github. Not too long afterwards, I tried to upon the .fla in the most recent version of Flash, without any luck. I have several projects that are stuck like this.

Not too long after I found my missing code cache, I updated a different project, a nonogram implementation in ActionScript, so that it could be compiled solely with the command line ActionScript compiler, mxmlc.

The Tetris project was more difficult to update because there were several assets created and stored in the .fla file, including some graphics. I decided to remove the dependency on the .fla and create a pure ActionScript application.

Some of the first things I did were to remove the fla from the project, and rearrange the source code into a package. I created a Makefile to run the compilation command, and setup guard to watch the source files, and rebuild the project upon change detection.

In addition to creating a package for all the source files, I also had to create a class out of the code that originally ran as part of the root MovieClip’s actions. Luckily, I had saved this script externally, and not inside of the .fla. I wrapped this in a Game class, and began debugging.

It took almost an hour to get the code to compile. Most of the issues were related to the transition from the ActionScript 2 to ActionScript 3. Some of the issues I ran into included:

  • MovieClip methods such as attachToMovieClip and createEmptyMovieClip were not available.
  • Warnings for functions without explicit return types
  • Use of “function(): Void” instead of “function(): void”
  • Warnings about lack of explicit types with variables
  • Changes to the scope of loop counter variables
  • Absence of the Key global
  • Changes to event binding

Once I had everything compiling, then I worked on run-time errors. These included:

  • Changes to “this” resolution (mostly having to do with event binding)
  • Weird issues with static variables
  • Changes to property definitions, for example from ._visible to .visible
  • Missing references referred to in Game.as (these were assets from the fla)

But part of this process was also getting familiar again with ActionScript. For example, I learned that an empty MovieClip has no width, even if you assign one explicitly.

Guard was incredibly helpful in resolving both the compile-time and run-time issues. I had the swf reloading every few seconds until I got the game loop working.

The end result is shown below, compared to the old swf. The game is now pure code, if somewhat uglier than it was. Mostly, I’m glad I can preserve some of my work without being locked in by a vendor. You can find the source on Github, and a video of the new app in operation on Youtube.

Tetris Flash - 2004

Tetris Flash – 2004


Tetris Flash Update

Tetris Flash Update 2013