Click here to Skip to main content
Click here to Skip to main content

BoxWorld (Sokoban)

By , 31 Oct 2011
 
Menu_small.jpg

Screenshot_small.jpg

Introduction

This application/game was designed as an experiment really! I remember playing this game on my graphing calculator many years ago. Never really played it since. And even with this, it's not so much as the game as it was making the game.

Credits to go out to: J. van den Bergh - (see About form in application for email address).

Without his work on the original game, this would have taken me a lot longer to get done. His graphics and graphics logic have been used. Originally, the game was in C# and was pretty simple. (No offence at all to the original author whatsoever - was an awesome app, I just wanted more!). I think the only code that exists of his is the Level and LevelSet code (now VB.NET - was C#).

Sorry about sticking the source and binaries on CodePlex - the downloads are reasonably large.

NOTE

The screenshots are of the DevExpress version. The version without DevExpress doesn't look as great, but functionality is still the same in most regards. Performance is slightly faster with data binding due to standard .NET controls having to rebind when a collection changes.

Background

What I added:

  • Rewrote the game from ground up
  • A cooler interface
  • Put in sounds
  • Added all options
  • Added high scores (well, it's more just a score history)
  • Added save game ability
  • Added zoom in/out (very awesome)
  • And heaps of other stuff

The quotes:

  • They're in a file in the Data directory.
  • File is called BoxWorld.Quotes.dat.
  • It's a simple CSV (pipe delimited - the | character).
  • Change it around, put your own quotes in.
  • When playing a game, quote changes by itself (about 20 seconds).
  • Click on the quote to get a new one.

To other developers:

  • You can create more graphics libraries.
  • Just copy the existing directory and go nuts.
  • Change Tada.wav to another sound (call it the same).
  • All data storage is done via XML.
  • Scores have been tested for performance to 100k records.
  • Zoom levels are per level. Very cool.

With the level files:

  • Look in the Data\Levels folder for the levels.
  • You can add your own levels.
  • The level files are XML.
  • Pretty easy to understand files.
  • Just drop the file in that directory.
  • Keep the level set title unique.

Important Note:

The included source code includes two projects. One project called LITE does not have DevExpress references and is built with standard .NET controls only. The DevExpress version looks a lot better, but not everyone has DevExpress.

The version without DevExpress is more or less the same as the one with the controls, but some of the binding is different. I've found the .NET controls (ListBox and ComboBox) tend to tend the datasource set to null before binding. Only seems to apply with lists of data.
Above all, enjoy playing it!

Acknowledgements

The Different Controls and Code

Controls

  • HighScores

    This control stores the high score data. Each player has their own settings file. A settings file stores data such as player ID, player name, high scores, zoom levels and general program options. When displaying high scores, all the player files are read and high scores are collated and presented.

  • Levels

    Really the main start screen. Shows level sets (a collection of levels) and levels. Also shows a preview of the level.

  • Options

    Lets you player configure options. Stores options per player.

  • PlayBoard

    This is the main control used to display the level.

  • Players

    The start screen. Used to select a player and maintain players. By default there is no option, but you can flick in the App.Config the option to turn passwords on. Passwords are saved in players XML file in encrypted form. Key is hardcoded in program.

  • SaveGames

    Shows a listing of saved games.

  • Welcome

    Simple control to display welcome text and provides a link to About form.

Controls

  • frmAbout

    Shows about text, level count and level set count.

  • frmMain

    Main screen of application.

  • frmPlayBoard

    Main play board control.

  • frmSplash

    Just the splash screen.

  • frmStart

    Start screen of application, lets user select and maintain users.

XML Serialization

All of the settings are saved in XML, all players have their individual XML files. Saving the XML looks like:

Public Shared Sub SavePlayer(MySettings As Models.Player)
    Dim xSerial As Serialization.XmlSerializer
    Dim objStreamWriter As IO.StreamWriter = Nothing
    Dim Location As String
    '
    Try
        Location = String.Format("{0}{1}.xml", _
        My.Application.PlayersDirectory, MySettings.PlayerID)
        objStreamWriter = New IO.StreamWriter(Location)
        xSerial = New Serialization.XmlSerializer(MySettings.GetType)
        xSerial.Serialize(objStreamWriter, MySettings)

    Catch ex As Exception
    Finally
        If (objStreamWriter IsNot Nothing) Then
            objStreamWriter.Close()
        End If
    End Try
End Sub

Essentially this code is building up the save location, creates a serializer with the Player model, then serializes the class to the XML file. So quick and easy.

And loading it:

Public Shared Function LoadPlayerSettings() As List(Of Models.Player)
    Dim fileList As String()
    Dim playerList As New List(Of Models.Player)
    Dim createPlayer As Boolean = False
    '
    Try
        ' find out if we should create players.
        fileList = System.IO.Directory.GetFiles(My.Application.PlayersDirectory)
        If (fileList Is Nothing) Then
            createPlayer = True
        Else
            If (fileList.Length = 0) Then
                createPlayer = True
            End If
        End If

        ' do we just create a player and add, or load up existing?
        If (createPlayer) Then
            Dim newPlayer As Models.Player
            newPlayer = GeneralHelper.CreatePlayer("Guest", String.Empty)
            playerList.Add(newPlayer)
            GeneralHelper.SavePlayer(newPlayer)
        Else
            For i As Integer = 0 To fileList.Length - 1
                Dim xSerial As Serialization.XmlSerializer
                Dim objStreamReader As StreamReader = Nothing
                '
                Try
                    Dim newPlayer As New Models.Player
                    objStreamReader = New StreamReader(fileList(i))
                    xSerial = New Serialization.XmlSerializer(newPlayer.GetType)
                    newPlayer = CType(xSerial.Deserialize(objStreamReader), Models.Player)
                    playerList.Add(newPlayer)

                Catch ex As Exception
                Finally
                    If (objStreamReader IsNot Nothing) Then
                        objStreamReader.Close()
                    End If
                End Try
            Next
        End If
    Catch ex As Exception
    End Try
    '
    Return playerList
End Function

Loading is a bit different as we get all the player XML files, if we don't have any, we always create a default player.

Skinning

You can create your own level graphics - just copy the Original directory instead graphics to the same directory (Graphics). Then just edit the files.

Running Timer

I actually found this code somewhere else, basically the play board has a TimeSpan on it (set to 0). I also have a Timer control on the board, and every 100 milliseconds, the TimeSpan gets 100 milliseconds added to it. When you save a game, the ticks of the TimeSpan is saved and when you resume, the ticks is loaded into the TimeSpan.

Serialize a Multidimensional Array to Base64 (i.e. Save Game)

Because some of the levels are just massive, I needed to implement the ability to save a game. The biggest thing was the array of level pieces.

Private _LevelData As Models.ItemType(,)
<Xml.Serialization.XmlIgnore()>
Public Property LevelData As Models.ItemType(,)
    Get
        Return Me._LevelData
    End Get
    Set(value As Models.ItemType(,))
        Me._LevelData = value
    End Set
End Property        
Public Property LevelData64 As String
    Get
        Dim format As Binary.BinaryFormatter
        Dim mStream As MemoryStream = Nothing
        Dim str_b64 As String = String.Empty
        Dim byArr As Byte()
        '
        Try
            format = New Binary.BinaryFormatter()
            mStream = New MemoryStream()
            format.Serialize(mStream, Me.LevelData)
            '
            byArr = mStream.ToArray()
            str_b64 = Convert.ToBase64String(byArr)
            
        Catch ex As Exception
        Finally
            mStream.Close()
            mStream.Dispose()
        End Try
        '
        Return str_b64
    End Get
    Set(value As String)
        Dim format As Binary.BinaryFormatter
        Dim mStream As MemoryStream = Nothing
        '
        Try
            format = New Binary.BinaryFormatter()
            mStream = New MemoryStream(Convert.FromBase64String(value))
            Me.LevelData = CType(format.Deserialize(mStream), ItemType(,))
            '
        Catch ex As Exception
        Finally
            mStream.Close()
            mStream.Dispose()
        End Try
    End Set
End Property

The code above is modified a bit, but hopefully that makes sense. Much the same as general serialization, but using a BinaryFormatter. Note the XmlIgnore bit - I actually want to ignore that specific property for serializing and have my own property for that.

Whenever you're playing a level or resuming a save game, make sure you copy the array and not just set the array because of references. With arrays, you're manipulating the actual source. Something like:

Array.Copy(LevelMap, Me._DefaultMap, LevelMap.Length)

That bit of code copies the LevelMap (position of all tiles) into a default level map we can use later when we reset levels.

Points of Interest

  • XML serialization is very cool.
  • Learnt how to use a TimeSpan to keep a running timer.
  • Learnt how to serialize a multi-dimensional array to base 64.
  • Learnt how to save the state of a game and resume.
  • There is some exception management, but it's not the best right now, may be improved upon later.

History

  • Version 8.0.1110.30 - Initial release

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

About the Author

Tyron Harford
Software Developer
New Zealand New Zealand
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionBrilliant.....memberGary Noble31 Oct '11 - 7:18 
Hi Tyron
 
Good to see your still codeing. This is pretty awesone.
 
Regards,
Gary
AnswerRe: Brilliant.....memberTyron Harford31 Oct '11 - 11:37 
Hey man!

Yep, still coding. This isn't as big as Ignition mind you!
Didn't take that long to make, but looking back, should have really made the GUI more replaceable (so you could then just put a different front end on with third party controls). The more stuff I thought up, the bigger the app got!

Wasn't initially designed to have player XML files (used to be a list of players within the settings file) but ended up better this way. Performance is slow in places (when auto advancing the levels - slow, should really just pick a new level rather then re-create the form all the time). Some of the loops are too intensive and caching needs to be implemented.

Actually thinking of getting into web development more, being doing WinForms programming for over 10 years now, I think the new HTML stuff will actually be quite cool.

With Ignition - that had some more development to it, but nothing major. The GUI has been updated to look more modern and events on the data layer were implemented at one point (so you could hook your own scripts and code into hardcoded functionality). If I get into web stuff more - I'll create a web interface for it, then that will probably go open source.
GeneralRe: Brilliant.....memberGary Noble1 Nov '11 - 0:12 
Tried sending you a couple of emails but they keep bouncing back.... I'd love to see the changes you made to ignition?
GeneralRe: Brilliant.....memberTyron Harford1 Nov '11 - 10:15 
Will send you an email in a bit - changed my email address.
 
Changes aren't big and currently they are in different projects, just haven't found time to go through the project properly. Actually thinking very seriously of rewriting some major parts of it and doing a web client in HTML5 type stuff. Would have to learn more web programming, but I think it could be well worth it.
QuestionLooks nice, but the source uses 3rd party DevExpress grid, so cant run the source codemvpSacha Barber31 Oct '11 - 6:18 
Is the DevExpress grid free?
Sacha Barber
  • Microsoft Visual C# MVP 2008-2011
  • Codeproject MVP 2008-2011
Open Source Projects
Cinch SL/WPF MVVM

Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

AnswerRe: Looks nice, but the source uses 3rd party DevExpress grid, so cant run the source codememberTyron Harford31 Oct '11 - 11:39 
Thanks man!

I did think about this when I put the source up. Initially I was going to be selfish and just use the game for myself, but thought it's be pretty cool for other people to see how something like this could be done. But I had already used DevExpress controls then.

So I quickly copied the project and took out the references (see the Lite project, included in the source) and that I confirm should have no DevExpress references. It doesn't look as nice, but the functionality is exactly the same. Didn't really know how to get around that I used them, so that was the best thing I could think of, just create a seperate project for people that don't have it and that version can be built by anyone.

Hope that helps!
In essence, there is a project included that can be run by anyone, just remove the project with DevExpress references to get started.
 
(that's essentially a super long answer to say, no, DevExpress isn't free Smile | :)
QuestionGreat GamementorDave Kerr31 Oct '11 - 4:22 
Brilliant game, very addictive! Did you use some sort of framework for the game or build everything from the ground up? It is very nicely done!

AnswerRe: Great GamememberTyron Harford31 Oct '11 - 11:37 
Thanks man!

Nah, no framework, everything built from the ground up. Did use the level and graphics code from the original project though. Now that you mentioned it, a framework would have been a better idea so other games could be written.

And thanks about the tip about the installer too! Forgot to mention it installs into Program Files so you would need admin rights. If you install it into APPDATA type thing you should be fine.
GeneralRe: Great GamementorDave Kerr31 Oct '11 - 11:53 
Well congratulations, I think the lobby for the game and all of the supporting stuff looks great, it would be easy to but the core game functionality in and leave it at that but the whole thing hangs together really well, I must be dumb though because I've only managed to do a couple of the levels so far Wink | ;)

SuggestionError Opening File For WritingmentorDave Kerr31 Oct '11 - 4:16 
ALL:
 
If you have 'Error Opening file For Writing' when running the installer, run it as an administrator!!

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 31 Oct 2011
Article Copyright 2011 by Tyron Harford
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid