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

BoxWorld (Sokoban)

, 31 Oct 2011
Rate this:
Please Sign up or sign in to vote.
BoxWorld game (Sokoban)
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)

Share

About the Author

Tyron Harford
Software Developer
New Zealand New Zealand
No Biography provided

Comments and Discussions

 
QuestionGreat Game PinmentorDave Kerr31-Oct-11 4:22 
AnswerRe: Great Game PinmemberTyron Harford31-Oct-11 11:37 
GeneralRe: Great Game PinmentorDave Kerr31-Oct-11 11:53 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140827.1 | Last Updated 31 Oct 2011
Article Copyright 2011 by Tyron Harford
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid