Tag Archives: HTTP request

Project 365

I keep a project 365 page, which means one photo per day. It’s a good way to keep track of the passage of time and as I take my camera everywhere with me it’ quite easy. After using a blog for the photos I soon became frustrated at how long it took, so I decided to make my own wrapper for the images.

Links

Live page
GitHub repository

Overview

The images are stored on disk, with automator scripts to resize them to the appropriate sizes. They are then arranged hierarchically according to date so that they can be arranged easily on the page. A list of comments, also arranged by date, is read via PHP and served up to the client as a Javascript file. There are event listeners added to each thumbnail so that the user can browse the images.

Challenges

Challenge: Given the large number of images there are a huge amount of HTTP requests.
Solution: Each thumbnail image comes with an overhead that could be reduced in the following way. The thumbnails can be combined into a single image which is indexed according to the date of each thumbnail. This can then be sliced up by client using a canvas and arranged on the page. Doing this would save a lot of time and bandwidth for both the client and server, but would require rewriting much of the code, and it is a low priority. (To be revisited.)

Screenshots

Screenshot of Project 365.
Screenshot of Project 365.
Screenshot of Project 365.
Screenshot of Project 365.

Work in progress: Trigger game

Like the Science Shift Simulator, this is a game that emerged as a subset of the aDetector project. The player has to save events that match the criteria given, just like a trigger does in real life. The results are then combined across all the players in a given team to determine the final score. The scores can be combined across experiments to make a “discovery”. This is still in development, which will continue over the net week and months.

Links

Live page
Spy page
GitHub repository

Overview

This is another cooperative multiplayer game aimed at showing the public (especially high school pupils) how particle physics research actually takes place. Any number of players can take part and they are split into “Team ATLAS” and “Team CMS”. The score of each team is determined by the performance of the players on each “shift” they take at the trigger, and the final scores are combined for the discovery of the Higgs boson. There is also a “spy” mode where people can see the events as they are submitted.

Challenges

Challenge: This project needs an attractive, fast, and realistic detector model.
Solution: Having already developed a decent detector model for the aDetector project, I simply used a two dimensional version for this project. I then split the detector finely in \(\eta\) and \(\phi\) to make interactions between the particles and detector easier. The aesthetics went through a few iterations before settling on the final design. However further optimisations and aesthetic changes are anticipated as development continues. (Resolved, to be revisited.)
Challenge: This game puts a bit of a strain on my server.
Solution: My web space uses a shared server, so sometimes many HTTP requests from the same client looks like a Denial Of Service (DOS) attack, resulting in a throttling of the requests. There are two main strategies to overcome this. The first option is to bundle up several requests into one request, reducing the total number of requests, and the load on the server. This solution has not been implemented yet. The second option is to change the server settings. I do not have access to these, but as development continues I intend to move to a different server that can handle so many very small requests. (To be revisited.)
Challenge: This game needs cross platform and cross device support.
Solution: This game was initially intended to be played with an iPad, but I did not have an iPad for testing. On the day of the release of the game I had to tweak the settings so the response times were slower with respect to mouse events to make it easier to play on a tablet device. These settings are trivially changed to allow multiple device support. (Resolved.)
Challenge: The game should be repsonsive to the inputs of the user.
Solution: Initially the game did not confirm success when a user clicked on the screen and this lead to confusion. As a result I had to add a big green “tick” for success and a big red “cross” for failure to inform the user of the status of the event. (Resolved.)
Challenge: The game needed an animated histogram for the final result.
Solution: I’ve made histogras before, including histograms that update themselves on the aDetector project, but until now I had not animated a histogram. This was a bit tricky as I had to call events which were using the this keyword, so the histogram object had to be stored as a global variable because, although I’d like to, I couldn’t use this to refer to the histgoram. Javascript can be frustating like that. (Resolved.)

Screenshots

I don’t normally put lots of screenshots up, but I’m quite proud of the asethetics here, so here are the three main screens of the main game:

The front page of the game.
The front page of the game.
The transition page of the game.
The transition page of the game.
The game in progress.
The game in progress.

The design went through a few iterations before settling on the current choice:

Design 1
Design 1
Design 2
Design 2
Design 3
Design 3

Work in progress: Science Shift Simulator

The Science Shift Simulator game is something that emerged from development of the aDetector project and LHC Driver projects. The mechanic is loosely based on the cooperative game Space Team. This is still in development, so the current page doesn’t actually work at the time of writing, and development will continue over the net week and months.

Links

Live page
GitHub repository

Overview

This is a cooperative multiplayer game aimed at showing the public (especially high school pupils) how particle physics research actually takes place, with an emphasis on cooperation. The game proceeds by bringing up several issues that need to be solved. The “Shift Leader” has information about how to solve the problems, but no control over the various subsystems. Instead they have a team of “Shifters” whose job is to solve the problems. This then becomes a problem of communication, cooperation, and optimisation between the various players.

For this project each player has control of a browser, which communicates with a central server. The Shift Leader’s client sends problems to the server, which are then collected by the Shifters’ clients. As a result the main difficulty in developing this game is synchronisation, especially when there are network problems!

Eventually this project will be reskinnable to allow other developers to add their own content. This will also be moved into a collaborative space, as this will have several developers in the future.

Challenges

Challenge: The big problem is synchronisation.
Solution: This is not the first project I’ve developed that required the client and server to communicate via AJAX requests, but it is the first that had more than one client sending information to the server. In fact it was this project that persuaded me to install MySQL locally and hook it up to Apache to get a local LAMP stack running on the my laptop. I got a prototype version working for demonstration purposes, but had to cut some corners. Development stopped in the middle of a significant rewriting of code, which is still to be completed. (To be revisited.)
Challenge: This game contains many mini games.
Solution: This is the first game I made that has mini games in it, and this required a different strategy for development. Making a generic mini game module in Javascript is not easy, and there will be some further significant changes as I rewrite this code again. (Resolved, to be revisited.)

Screenshot

Here’s the page as it currently looks. It needs a bit of a facelift!

Screenshot of the Shift Leader's page
Screenshot of the Shift Leader’s page

greaseBox

In the mid to late 2000s I used to visit an LGBT social networking site, Thingbox. The site had many features, but the users wanted more. It was around this time that I was discovering how to use greasemonkey, so I put together many short scripts. Eventually I combined them all into a single script called greasebox that was very popular. It has been many years since it was maintained though, so I doubt it still fully works.

Links

Thingbox
Live page
GitHub repository

Overview

The website provides a huge amount of information via AJAX and HTTP requests, and this project required some clever reverse engineering to get right. When the scripts are combined they are modular, and they are executed in a specific order to prevent clashes. A few modules are inter dependent, so they are arranged in the correct order, otherwise it goes from least to most fragile. There is also a debug tool to see exactly whre things go wrong. There is also a detailed interface page where users can change their settings.

Unfortunately I don’t have a screenshot of the project, since it was developed so long ago (last touched 2011/02/05) and in browser scripting has changed a lot since then.

Challenges

Challenge: This had to use the Thingbox “API” properly.
Solution: Although Thingbox didn’t have an official API, I reverse engineered how things worked to make it do what I wanted. This occasionally required help from other users (eg to send friend requests) but was mostly possible on my own. (Resolved.)
Challenge: The content had to be intuitive and responsive to users’s needs.
Solution: This was one of the first projects where I had to see things from the point of view of the user and make things as intuitive as possible. The aesthetics of the project had to fit in with the Thingbox style, not only to look good, but to be usable. This meant that I had to deconstruct and understand the rationale behind the site design and the choices the creators made, which was very informative. I also had to pay close attention to requests and questions from users as they arose, which gave me a lot of insight into how to make things as easy to use as possible. (Resolved.)
Challenge: When combining modules the order mattered.
Solution: I had several modules that would go through and edit the content of the page. Obviously this would cause problems if it wasn’t done carefully, so I had to order the modules in the correct way. This was not trivial to do, but made management much simpler. There was also a dedicated debug module that would take information from the current session and print it to a div element that the user could attach to the page with a special command. This debug information allowed me to determine exactly how far the scripts got before a module caused a problem. (Resolved.)
Challenge: This project suffered from a sense of humour.
Solution: I was writing for a group of mostly well educated middle aged gay men, and as a result there was a lot of banter and humour. I would create puns for each of the single modules. (For example one of the running jokes was constant references to internalised homophobia, so I called the script that open external links in a new window “externalised homophobia”.) This did not help when I tried to combine the modules into a single script, and I will not be making that mistake again! (Resolved.)
Challenge: This needed cross platform and cross browser support.
Solution: This was probably the most difficult challenge to overcome. I would test in Firefox with a mac, but there were many users who insisted on using Window, Chrome, and sometimes even Safari. Each one had different nuances (including counter intuitive storage of information) that had to be understood to make features work. Although I tried very hard to make it work, there were some features that didn’t make it onto every browser. (Mostly resolved.)

91 map

I played the game 91 and wanted to create a map of the world. What started out as a simple map to help keep track of the regions soon turned into a larger project which resulted in an A0 sized poster being produced. The creator of the game got in touch and at the time of writing the first poster has been printed and more will follow. The creator is going to publish and sell these posters online.

Links

Live page
Jayisgames review
Play 91 online
GitHub repository

Overview

The game 91 is a tile based canvas game which is written entirely in Javascript (served up by PHP via AJAX requests). The game itself has a “fog” which means that only regions in a line of sight are visible. Given the way the game is organised means that in principle it should be easy to create a map. I adapted the code to make maps of each region, and then created a patchwork of all these small maps to create a larger map.

Challenges

Challenge: The code had to be reverse engineered to obtain all the maps.
Solution: One of the most fun parts of this project was reverse engineering how the AJAX requests were handled and how the maps were parsed. Fortunately it was relatively straightforward to do. (Resolved.)
Challenge: Small maps had to be combined with different drawing styles.
Solution: To make the large map I had to combined many smaller maps. There’s no “cheap” way to do this, so I created an object that contained the smaller maps in a larger two dimensional array where each cell has its own drawing style flag, allowing non-trivial overlap of different drawing styles. (Resolved.)
Challenge: A poster of very large dimensions needed to be created.
Solution: There’s no easy way to manage an image of the size needed to make a large poster. I have made large posters before (for example, in the Marble Hornets project) so I was prepared for many of the problems. The sides of the map had to have relative sizes of \(\sqrt{2}\), which fortunately was relatively easy (especially when compared to the Citadel project. In the end I opted to have a large .png file at \(\sim 500 ~\mathrm{dpi}\) giving very fine print quality. The resulting file is just under 20 MB in size, which is rather impressive given its physical size. (Resolved.)

Sample outputs

Bacalhau Island map
Bacalhau Island map
Bateman Tower map
Bateman Tower map
Small poster, first draft
Small poster, first draft

The final version of the poster, as approved by the game’s creator, can be seen here: Poster version 2.

Tetris

This was my first serious Javascript project which was written some time in 2009. I chose to write a Tetris clone because it was a well defined project that would teach me how to use Javascript. In addition to Tetris I also made Tritris and Pentris to see how well balanced the three games are compared to each other. It turns out that Tetris is about right, with Tritris being too easy and Pentris being too hard.

Links

Live page
GitHub repository

Overview

The user plays the game with the arrow keys, and the game gets slowly faster as their score increases. There is a MySQL and PHP backend to save scores on the server. As much as possible, the three games have been harmonised so that they use the same page, the same Javascript library, and the same PHP page for interaction with the server. This is outlined in a previous post where I discussed how the code was refactored. This is one of my favourite projects, as it’s one of the few “complete” projects that touches on almost all of Javascript, with some HTML, CSS, PHP, MySQL, httpxml, and cookies also thrown in there. This project taught me so much about Javascript and was an excellent start with the language.

Challenges

Challenge: This project required learning how to use Javascript.
Solution: What a challenge! Having worked previously with C++, I found that Javascript was rather easy to learn, and quickly came across its peculiarities and limitations. (Resolved)
Challenge: The project required detailed manipulation of the DOM.
Solution: It was with this project that I learned how to use the DOM, which helped me to better understand the heirarchical structure of XML in general. I also wanted the HTML to be semantically pure, so while I used the DOM to store some information about the state of the game, I also ensured that it was semantically consistent. (Resolved)
Challenge: This game required careful control of Javascript events and synchronisation.
Solution: This was probably the most difficult and instructive part of the project. I had to learn how to register event handlers in a manner which worked across browsers. I still use the same style of event handling today that I developed when I wrote this project. It took a while to get used to the issues of synchronisation using the window.setTimeout method, which I still use frequently today. (Resolved)
Challenge: I had to store some data on the server.
Solution: I had had plenty of experience with PHP and MySQL before this project, including sanitising input to the database, so the PHP side of this challenge was easy to implement. However making the httpxml requests was not so easy and took some practice. After a few iterations I got a working model, although this is something I should improve further, as httpxml requests tend to be rather messy. (Resolved)
Challenge: One of the users wanted a feature that required cookies.
Solution: One user spent so long playing the game that he wanted to be able to “block” himself. As a result I had to implement a feature hat sets a cookie that prevents the user from playing. This was the first time I had set and read cookies using Javascript, and not something I have had much use for since. (Resolved)
Challenge: The game has a soundtrack.
Solution: Having used so many feature of Javascript, I wanted to add some music. This is far from trivial in the world of Javascript, and not so easy in the days befre embedded YouTube videos. Although support is a little shaky, the music was added and an interface included. (Resolved)
Challenge: The game had to have cross browser support.
Solution: This game was initially developed using Firefox, but one of the users wanted it to work with Chrome. This was the first time I met the frustration of cross browser event handling, which has been something of a pain ever since, but it was not too hard to overcome. (Resolved)

Screenshot

Screenshot of the Tetris game
Screenshot of the Tetris game

Code update: Tetris

The first major Javascript project I took on was a clone of Tetris. Every time I look back at it I’m actually surprised an impressed at how many features it has. For a first project it has DOM manipulation, httpxml requests, timeout functions, cookies, audio, and a MySQL backend. After making the Tetris clone I then tried to make Pentris, which is the same game but with five blocks per piece. In going from Tetris to Pentris I made some interesting optimisations.

The Tetris clone
The Tetris clone

For example, here is the function tryRotate:

function tryRotate(){
  switch(color){
    case "red":
    switch(orientation){
      case 0:
      case 2:
        rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] + 0 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 1 ;
        rotatedC[2] = C[2] - 1 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] - 2 ; rotatedR[3] = R[3] + 1 ;
      break ;
      case 1:
      case 3:
        rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] + 0 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] - 1 ;
        rotatedC[2] = C[2] + 1 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] + 2 ; rotatedR[3] = R[3] - 1 ;
      break ;
    }
    break ;
    
    case "green":
    switch(orientation){
      case 0:
        rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] + 1 ;
        rotatedC[1] = C[1] + 1 ; rotatedR[1] = R[1] - 1 ;
        rotatedC[2] = C[2] + 0 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] - 1 ; rotatedR[3] = R[3] + 1 ;
      break ;
      case 1:
        rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] + 1 ;
        rotatedC[1] = C[1] + 1 ; rotatedR[1] = R[1] + 1 ;
        rotatedC[2] = C[2] + 0 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] - 1 ; rotatedR[3] = R[3] - 1 ;
      break ;
      case 2:
        rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] - 1 ;
        rotatedC[1] = C[1] - 1 ; rotatedR[1] = R[1] + 1 ;
        rotatedC[2] = C[2] + 0 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] + 1 ; rotatedR[3] = R[3] - 1 ;
      break ;
      case 3:
        rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] - 1 ;
        rotatedC[1] = C[1] - 1 ; rotatedR[1] = R[1] - 1 ;
        rotatedC[2] = C[2] + 0 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] + 1 ; rotatedR[3] = R[3] + 1 ;
      break ;
    }
    break ;
    
    case "blue":
    switch(orientation){
      case 0:
        rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] + 0 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 1 ;
        rotatedC[2] = C[2] - 1 ; rotatedR[2] = R[2] + 2 ;
        rotatedC[3] = C[3] - 2 ; rotatedR[3] = R[3] + 1 ;
      break ;
      case 1:
        rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] + 1 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 0 ;
        rotatedC[2] = C[2] - 1 ; rotatedR[2] = R[2] - 1 ;
        rotatedC[3] = C[3] + 0 ; rotatedR[3] = R[3] - 2 ;
      break ;
      case 2:
        rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] + 1 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 0 ;
        rotatedC[2] = C[2] + 1 ; rotatedR[2] = R[2] - 1 ;
        rotatedC[3] = C[3] + 2 ; rotatedR[3] = R[3] + 0 ;
      break ;
      case 3:
        rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] - 2 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] - 1 ;
        rotatedC[2] = C[2] + 1 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] + 0 ; rotatedR[3] = R[3] + 1 ;
      break ;
    }
    break ;
    
    case "cyan":
    switch(orientation){
      case 0:
        rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] + 2 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 1 ;
        rotatedC[2] = C[2] + 1 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] + 0 ; rotatedR[3] = R[3] - 1 ;
      break ;
      case 1:
        rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] - 1 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 0 ;
        rotatedC[2] = C[2] + 1 ; rotatedR[2] = R[2] + 1 ;
        rotatedC[3] = C[3] + 2 ; rotatedR[3] = R[3] + 0 ;
      break ;
      case 2:
        rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] - 1 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 0 ;
        rotatedC[2] = C[2] - 1 ; rotatedR[2] = R[2] + 1 ;
        rotatedC[3] = C[3] + 0 ; rotatedR[3] = R[3] + 2 ;
      break ;
      case 3:
        rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] + 0 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] - 1 ;
        rotatedC[2] = C[2] - 1 ; rotatedR[2] = R[2] - 2 ;
        rotatedC[3] = C[3] - 2 ; rotatedR[3] = R[3] - 1 ;
      break ;
    }
    break ;
    
    case "magenta":
    switch(orientation){
      case 0:
      case 2:
        rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] + 0 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 1 ;
        rotatedC[2] = C[2] + 1 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] + 2 ; rotatedR[3] = R[3] + 1 ;
      break ;
      case 1:
      case 3:
        rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] + 0 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] - 1 ;
        rotatedC[2] = C[2] - 1 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] - 2 ; rotatedR[3] = R[3] - 1 ;
      break ;
    }
    break ;
    
    case "yellow":
    switch(orientation){
      case 0:
      case 1:
      case 2:
      case 3:
        rotatedC[0] = C[0] + 0 ; rotatedR[0] = R[0] + 0 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 0 ;
        rotatedC[2] = C[2] + 0 ; rotatedR[2] = R[2] + 0 ;
        rotatedC[3] = C[3] + 0 ; rotatedR[3] = R[3] + 0 ;
      break ;
    }
    break ;
    
    case "white":
    switch(orientation){
      case 0:
      case 2:
        rotatedC[0] = C[0] + 1 ; rotatedR[0] = R[0] - 1 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 0 ;
        rotatedC[2] = C[2] - 1 ; rotatedR[2] = R[2] + 1 ;
        rotatedC[3] = C[3] - 2 ; rotatedR[3] = R[3] + 2 ;
      break ;
      case 1:
      case 3:
        rotatedC[0] = C[0] - 1 ; rotatedR[0] = R[0] + 1 ;
        rotatedC[1] = C[1] + 0 ; rotatedR[1] = R[1] + 0 ;
        rotatedC[2] = C[2] + 1 ; rotatedR[2] = R[2] - 1 ;
        rotatedC[3] = C[3] + 2 ; rotatedR[3] = R[3] - 2 ;
      break ;
    }
  break ;
  }
  for(var i=0 ; i<4 ; i++){
    if(rotatedR[i]>nRows) return false ;
    if(rotatedR[i]<1)     return false ;
    if(rotatedC[i]>nCols) return false ;
    if(rotatedC[i]<1)     return false ;
    var skip = 0 ;
    for(var j=0 ; j<4 ; j++){ if(rotatedR[i]==R[j] && rotatedC[i]==C[j]) skip = 1 ; }
    if(skip==0){
      var className = getClass(rotatedR[i],rotatedC[i]) ;
      if(className!="empty") return false ;
    }
  }
  for(var i=0 ; i<4 ; i++){
    set(R[i],C[i], "empty") ;
    R[i] = rotatedR[i] ;
    C[i] = rotatedC[i] ;
  }
  for(var i=0 ; i<4 ; i++){
    set(R[i],C[i], color) ;
  }
  orientation++ ;
  if(orientation==4) orientation = 0 ;
}

Compare that to the streamlined version I had for Pentris:

function tryRotate(){
  rotatePiece() ;
  for(var i=0 ; i<n ; i++){
    if(rotatedR[i]>nRows) return false ;
    if(rotatedR[i]<1)     return false ;
    if(rotatedC[i]>nCols) return false ;
    if(rotatedC[i]<1)     return false ;
    var skip = 0 ;
    for(var j=0 ; j<n ; j++){ if(rotatedR[i]==R[j] && rotatedC[i]==C[j]) skip = 1 ; }
    if(skip==0){
      var className = getClass(rotatedR[i],rotatedC[i]) ;
      if(className!="empty") return false ;
    }
  }
  for(var i=0 ; i<n ; i++){
    set(R[i],C[i], "empty") ;
    R[i] = rotatedR[i] ;
    C[i] = rotatedC[i] ;
  }
  for(var i=0 ; i<n ; i++){ set(R[i],C[i], color) ;}
}
function rotatePiece(){
  for(var i=0 ; i<n ; i++){
    rotatedR[i] =  (Y) + C[i] - (X) ;
    rotatedC[i] =  (X) - R[i] + (Y) ;
  }
}

The optimisation here is quite impressive, and very much needed when you consider that the move from Tetris to Pentris means moving from five pieces of four blocks each, to eighteeen pieces of five blocks each, with the space needed for ratotions scaling from sixteen to twenty five.

In the first function the different scenarios are all checked explicitly, whereas in the second function the temporary piece is rotated and then collisions checked, before updating the main piece. The amount of code when from 82 to 27 lines, and in doing so became safer and more easily extendible.

In a similar vein, here's the updatePreview function, first in Tetris:

function updatePreview(){
  for(var i=1 ; i<3 ; i++){ for(var j=1 ; j<5 ; j++){ setPreview(i, j, "empty") ; } }
  switch(nextPiece){
    case 1:
      setPreview(1, 2, "red") ;
      setPreview(1, 3, "red") ;
      setPreview(2, 3, "red") ;
      setPreview(2, 4, "red") ;
      break ;
    case 2:
      setPreview(1, 3, "green") ;
      setPreview(2, 2, "green") ;
      setPreview(2, 3, "green") ;
      setPreview(2, 4, "green") ;
      break ;
    case 3:
      setPreview(1, 1, "blue") ;
      setPreview(1, 2, "blue") ;
      setPreview(1, 3, "blue") ;
      setPreview(2, 3, "blue") ;
      break ;
    case 4:
      setPreview(1, 3, "cyan") ;
      setPreview(1, 2, "cyan") ;
      setPreview(1, 1, "cyan") ;
      setPreview(2, 1, "cyan") ;
      break ;
    case 5:
      setPreview(1, 3, "magenta") ;
      setPreview(1, 2, "magenta") ;
      setPreview(2, 2, "magenta") ;
      setPreview(2, 1, "magenta") ;
      break ;
    case 6:
      setPreview(1, 2, "yellow") ;
      setPreview(1, 3, "yellow") ;
      setPreview(2, 3, "yellow") ;
      setPreview(2, 2, "yellow") ;
      break ;
    case 7:
      setPreview(2, 1, "white") ;
      setPreview(2, 2, "white") ;
      setPreview(2, 3, "white") ;
      setPreview(2, 4, "white") ;
      break ;
  }
}

Second in Pentris:

function updatePreview(){
  for(var i=1 ; i<4 ; i++){ for(var j=1 ; j<6 ; j++){ setPreview(i, j, "empty") ; } }
  var x = new Array(n) ;
  var y = new Array(n) ;
  var piece = getPiece(nextPiece) ;
  var counter = 0 ;
  for(var i=0 ; i<n ; i++){
    for(var j=0 ; j<n ; j++){
      if(piece[i][j]==1){
        x[counter] = i   ;
        y[counter] = j+1 ;
        counter++ ;
      }
    }
  }
  for(var i=0 ; i<n ; i++){setPreview(x[i],y[i],colors[nextPiece-1]);}
}

Such an economy of code! One of my favourite parts of code is refactoring existing blocks of code into something more elegant and effiienct. The original code, as a first attempt, and a first adventure in serious Javascript was necessarily messy and in need of a good clean, so it's no surprise that so many improvements could be had.

I've been putting off resolving these differences for a very long time, partly because the games work as they are and there's no need to fix something that's not broken, and partly because I wanted to keep some legacy code around to remind myself of how it all started. I've archived the original code and now I'm bringing the Tetris, Pentris, and Tritris into line with each other to make all three games share a single code base.

SCP

Here is where I put my content relevant to the SCP Wiki.

Links

Live page
SCP Wiki
GitHub repository

Overview

The SCP Wiki is description of a fictional emergency containment facility. This project collects mostly attempts at writing articles, and anything else relevant to the SCP, including the python script.

Challenges

Challenge: The python script has to make an HTTP request and keep the user entertained while it handles the request.
Solution: This was the first time I used python with HTTP requests, which turned out to be quite easy. The script produces a series of messages to emulate the security steps needed to access the articles. (Resolved)
Challenge: It would be nice to have ASCII images appear.
Solution: Most SCP articles contain small images, and I’ve already worked with ASCII art elsewhere, so it should be a matter of putting the two together, when I get time. (To be done)

python screenshot

SCPython screenshot
SCPython screenshot

Daily Fail

This is one of my more puerile projects. My older sister asked me if I could make a website that replaced the word “cancer” with the words “uncontrollable urge to bum farm animals” for Daily Mail articles. Seeing this as a chance to practice page scrubbing I took up the challenge and gave my sister a new toy.

Links

Live page
GitHub repository

Overview

The user supplies a Daily Mail uri and the script fetches the relevant page. It then performs a mildly intelligent search and replace to make the word substituions.

Challenges

Challenge: The script must get the style, scripts, and images correct.
Solution: The script goes through the source code and removes all scripts safely. The uris and images are then remapped to give tne user a seamless experience. Page scrubbing has never been so easy! (Resolved)

Sample output

Daily Fail sample headline
Daily Fail sample headline

Mandelbrot explorer

This project estimates the Mandelbrot set using the HTML5 canvas. It’s one of my longest running projects that has been implemented in PHP, SVG, HTML5 canvas, HTML tables, and even in ROOT.

Links

Live page
GitHub repository

Overview

This projects presents a wide range of different challenges. The aim is to create a fractal browser that pushes the operating environment to the limit in terms of performance, while still being user friendly and responsive. In its current iteration the user clicks on the region of the fractal they wish to explore and the project zooms in to that region. The user can change the way the fractal is coloured by changing the palette its properties. They can also move from the Mandelbrot set to the corresponding Julia set. There is also the option to explore the cubic Mandelbrot set. Past iterations have included even more fractals, including Newton iterations and generalised Julia sets. However these have been removed in this iteration as they should be refactored into a separate fractal class rather than inserted by hand.

Challenges

Challenge: The algorithm must be responsive and make reasonable use of memory.
Solution: The iterations used in the algorithm can be expensive when the image approaches several hundred thousand pixels. Currently the algorithm uses a pixel object to manage the colour at a given point, and for arbitrarily large images a single pixel object is used in order to reduce the cost of creating and storing large numbers of these objects. The current iteration uses Javascript in the user’s browser, and most modern browsers deal with excessive memory usage sensibly, killing particularly bad cases. It is not desirable to cap a user’s capabulities when it comes to image size, so instead the algorithm forces the browser to fail relatively safely and without major inconvenience. Previously this project ran on PHP on a shared server, so memory use had to be monitored and was formally enforced on the server, making failure modes potentially dangerous. Once the canvas became available I switched to using it very quickly. Even so, running PHP locally overnight to generate very large images is still a sensible use of resounces. There are probably other areas where savings could be made. (Resolved, to be revisited)
Challenge: The algorithm must make reasonable use of CPU.
Solution: In many cases the fractals take several tens of thousands of iterations per pixel for several thousand pixels, leading to large CPU usage. In the context of Javascript this can cause serious performance issues for the user, affecting their whole computing experience and not just that associated with the browser session. To mitigate this the iterations are interrupted every \(100 ms\) and forced to wait for \(10 ms\) before continuing. In addition when several small canvases are populated they are pushed into a queue which is processed serially with interruptions. This reduces the impact on the user’s CPU significantly leading to much smoother performance and better response to user input. Even so, this should be revisited to make further savings and be more responsive to the user’s inputs. (Resolved, to be revisited)
Challenge: The user interface must be intuitive.
Solution: In some senses it will never be possible ot make the user interface entirely transparent, given the technical nature of the fractal’s inner workings. In spite of this the way the user navigates is relativelt straightforward, but more improvments can and should be made. (Resolved, to be revisited)
Challenge: The palettes should be easy to edit.
Solution: The asethetic properties of the fractals often depend on the choice of palette. The palette scales can be manipulated, using slders on the log scale. This solution borrows from another project being developed in parallel, and leads to an easier interpretation of the scaling and distorting of the palette scale. This method should be tested in a “focus group” style environment. The user should be able to create and store a palette from scratch with their own choice of colours stops. (Resolved, to be revisited)
Challenge: The user should be able to store fractals.
Solution: The user can currently choose to save a fractal to the server, storing the \((x,y)\) coordinates, zoom, and other factors. This uses AJAX requests with a PHP and MySQL backend, which has become fairly standard in my projects by now. This comes with the usual MySQL injection overheads and PHP safety issues. In the future, as the number of fractals in the gallery increases, the gallery should be orgainsed in some manner to reduce bandwidth and CPU usage. (Resolved, to be revisited)
Challenge: The project should support arbitrary fractals for future expansion.
Solution: At the moment the fractal algorithms are hard coded into the project. This needs to be more object oriented in the future so that other developers can contribute their own fractals. (Partially resolved, to be revisited)

Sample output

Mandelbrot explorer output
Mandelbrot explorer output