C++ to C# Port

As part of my project to put Panzer General on the Windows Phone, I need a way to parse the map files from the original game.  Unlike the scenario files or the unit data file, the files are not in ASCII:

image

I ran across a map editor by Frederick Chlanda  that included source code – the only hitch is that it was written in C++.  Dusting off those brain cells, I spent some time isolating the area where the map file is parsed.  Here is what the code looks like:

void __fastcall TForm4::GetTrialBtnClick(TObject *Sender) { FILE *inf; //we get the set file //read in the Mapxx.set OpenDialog1->Title="Open .set"; OpenDialog1->Filter="*.set (*.set)|*.set|All files (*.*)|*.*"; if (!OpenDialog1->Execute()) return; inf=fopen(OpenDialog1->FileName.c_str(),"rb"); //0x65 is width and 0x67 is the height fseek(inf,0x65,SEEK_SET); fread(&SmMapX,2,1,inf); fread(&SmMapY,2,1,inf); ++SmMapX; ++SmMapY; SmMapT=SmMapY*SmMapX; //load entire 123 btyes of file start fseek(inf,0,SEEK_SET); for (int x=0; x<123; ++x) fread(&(SmStartup[x]),1,1,inf); //set the pointer to start of field fseek(inf,123+5*SmMapT,SEEK_SET); for (int y=0; y<SmMapY; ++y) for (int x=0; x<SmMapX; ++x) fread(&(SmTil[x][y]),2,1,inf); //get the country info fseek(inf,123+3*SmMapX*SmMapY,SEEK_SET); for (int y=0; y<SmMapY; ++y) for (int x=0; x<SmMapX; ++x) fread(&(SmOwn[x][y]),1,1,inf); //get the gln numbers (kind or name) fseek(inf,123+0*SmMapX*SmMapY,SEEK_SET); for (int y=0; y<SmMapY; ++y) for (int x=0; x<SmMapX; ++x) fread(&(SmGln[x][y]),2,1,inf); //get the side info fseek(inf,123+4*SmMapX*SmMapY,SEEK_SET); for (int y=0; y<SmMapY; ++y) for (int x=0; x<SmMapX; ++x) fread(&(SmSide[x][y]),1,1,inf); //get the road connectivity fseek(inf,123+2*SmMapX*SmMapY,SEEK_SET); for (int y=0; y<SmMapY; ++y) for (int x=0; x<SmMapX; ++x) fread(&(SmRdc[x][y]),1,1,inf); fclose(inf); OpenDialog1->Title="Open .stm"; OpenDialog1->Filter="Mxxxx.stm (*.stm)|*.stm|All files (*.*)|*.*"; if (!OpenDialog1->Execute()) return; inf=fopen(OpenDialog1->FileName.c_str(),"rb"); for (int y=0; y<SmMapY; ++y) for (int x=0; x<SmMapX; ++x) { fread(&(SmUtr[x][y]),2,1,inf); } fclose(inf); SmShowMap(); }

I spent some digging up the conversion – the hex values sprinkled in as magic numbers, the cryptic naming conventions, the unsigned 2-byte integers that needed to be converted to C# , and my own clumbsiness with pointers and references all posed a challenge.  However, in about 2 days of semi-intermittent work on this, I got myself an adequate port:

private static void ReadMapFile() { String filePath = @"C:\Users\Jamie\Documents\Visual Studio 2010\Projects\Tff.Panzer.ParseEquipmentFile\Tff.Panzer.ParseEquipmentFile\MAP01.SET"; FileStream fileStream = File.OpenRead(filePath); BinaryReader binaryReader = new BinaryReader(fileStream); Byte[] inputData = binaryReader.ReadBytes(10000); int sizeOfMapX = Convert.ToInt32(inputData[101]); int sizeOfMapY = Convert.ToInt32(inputData[103]); int totalNumberOfHexes = (sizeOfMapX + 1) * (sizeOfMapY + 1); string rowOutput = String.Empty; string rowSql = String.Empty; short[,] TileNumbers = new short[sizeOfMapX+1, sizeOfMapY+1]; short currentTileValue = 0; int currentPosition = 123 + (5*totalNumberOfHexes); for (int y = 0; y <= sizeOfMapY; ++y) { for (int x = 0; x <= sizeOfMapX; ++x) { currentTileValue = (short)inputData[currentPosition]; currentTileValue++; TileNumbers[x, y] = currentTileValue; rowOutput += currentTileValue + "\t"; currentPosition += 2; } Console.WriteLine(rowOutput); Debug.WriteLine(rowOutput); rowOutput = String.Empty; } }

This exercise makes me appreciate C# even more.  In any event, here is the parsed output:

image

I then threw in some Linq to Entities statement and persisted all of the unstructured data into a local instance of SQL Server.  I am up in the air about if I want to keep all of the game data in SQL Server on the phone or push the data to XML files.  Turfing that decision, I put the 1st scenario on the phone and this is what I got:

image

 

Which is pretty close to the original game:

image

Phidget Force Sensor Programming

For the next TriNUG main meeting, I thought of have a Family Feud type game. To that end, I wanted to simulate the buzzer/button that the contestants use:

image

To that end, I purchased 2 force sensors from Phidgets and hooked them up to my 8/8/8 Interface kit. I guess the contestants will use a finger versus their entire hand:

image

The sensor is very straight forward – you press the button and the Interface kit reads how much pressure is applied:

image

Thinking on how it would work as FamilyFeud buzzer system, I just need to keep track of who applied force 1st – the amount of pressure is irrelevant. Also, I need a trigger mechanism that starts listening at certain points – so that contestants don’t jump the gun.

I first started with a WPF project to see if I could hook into the Phidget InterfaceKit. I created a basic WPF application and wrote the following code (pretty much straight from the programmers guide):

public partial class MainWindow : Window { InterfaceKit _interfaceKit; public MainWindow() { InitializeComponent(); _interfaceKit = new InterfaceKit(); _interfaceKit.open(); _interfaceKit.waitForAttachment(3000); _interfaceKit.SensorChange += new SensorChangeEventHandler(_interfaceKit_SensorChange); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(String.Format("Name={0}",_interfaceKit.Name)); stringBuilder.Append(Environment.NewLine); stringBuilder.Append(String.Format("Label={0}", _interfaceKit.Label)); this.InterfaceKitInfoLabel.Content = stringBuilder.ToString(); } void _interfaceKit_SensorChange(object sender, SensorChangeEventArgs e) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append(String.Format("Index={0}", e.Index)); stringBuilder.Append(Environment.NewLine); stringBuilder.Append(String.Format("Value={0}", e.Value)); this.PressureSensor01Label.Content = stringBuilder.ToString(); } }

When I hit F5 and pressed a sensor, I got this error:

 

image

Ugh! I looks like Phidgets programming need to support thread affinity?! To confirm, I wrote a Console application that does pretty much the exact same thing:

static void Main(string[] args) { Console.WriteLine("Start"); InterfaceKit interfaceKit = new InterfaceKit(); interfaceKit.Attach += new Phidgets.Events.AttachEventHandler(interfaceKit_Attach); interfaceKit.SensorChange += new Phidgets.Events.SensorChangeEventHandler(interfaceKit_SensorChange); interfaceKit.open(); interfaceKit.waitForAttachment(); Console.WriteLine("-End-"); Console.ReadLine(); } static void interfaceKit_Attach(object sender, Phidgets.Events.AttachEventArgs e) { Console.Write(String.Format("Interface Kit {0} attached",e.Device.Name)); } static void interfaceKit_SensorChange(object sender, Phidgets.Events.SensorChangeEventArgs e) { Console.WriteLine(String.Format("Sensor {0} changed with the value {1}.",e.Index, e.Value)); } }

When I F5 and press the sensor, I get the following:

image

So it looks like I need to be thread-aware on WPF applications. After some digging into the code samples from the most recent documentation, I found this nugget of a project:

image

Sure enough, the code has thread-affinity:

void ifk_Attach(object sender, AttachEventArgs e) { this.Dispatcher.Invoke(new Action(delegate() { makeDigiInArrays(); makeDigiOutArrays(); makeSensorInArrays(); InterfaceKit attached = (InterfaceKit)sender; attachedLbl.Content = attached.Attached.ToString(); nameLbl.Content = attached.Name; serialNoLbl.Content = attached.SerialNumber.ToString(); versionLbl.Content = attached.Version.ToString(); digitalInputCountLbl.Content = attached.inputs.Count.ToString(); digitalOutputCountLbl.Content = attached.outputs.Count.ToString(); sensorCountLbl.Content = attached.sensors.Count.ToString(); for (int i = 0; i < attached.inputs.Count; i++) { digiInChkArray[i].Visibility = System.Windows.Visibility.Visible; digiInLblArray[i].Visibility = System.Windows.Visibility.Visible;

So this simple project just jumped up a notch if I want to use WPF.

MVVM && Game Design: Part 2

If you read this post that I wrote last week about building a game on Windows Phone 7 using MVVM, you know that I am trying to make a port of Panzer General to the Windows Phone.  Where I left it, I had made a repeating sets of hexagons to be the game board and I dynamically filled them based on the user’s touch.  My next step was to make the hexagons contain images of the terrain.  To that end, I snipped out a single hex of terrain and placed it in the images folder.  I then coded up this in the view hex:

Added this: <Image Canvas.Left="0" Canvas.Top="0" Height="100" x:Name="image1" Width="100" Stretch="Fill" Source="/Tff.Panzer;component/Images/Coast01.png" />

And I got this:

image

So then I realized that I need to add the image to the path, not layer it on top of the canvas:

<Path x:Name="PathRoot" IsHitTestVisible="True" Width="100" Height="100" d:LayoutOverrides="None" d:LastTangent="0,0" Stroke="Black" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0" Stretch="Fill" Data="M0,8.660254 L5,17.320508 15,17.320508 20,8.660254 15,0 5,0 0,8.660254 z" Canvas.Left="{Binding Path=CanvasLeft}" Canvas.Top="{Binding Path=CanvasTop}"> <Path.Fill > <ImageBrush ImageSource="/Tff.Panzer;component/Images/Coast01.png" /> </Path.Fill > </Path>

Doing that, I got this:

image

Well How about that!

There are 3 different ways to get this in markup:

Fill="{Binding Path=TerraineImage}"> <!--Fill="{Binding Path=Fill}"--> <!--<Path.Fill > <ImageBrush ImageSource="{Binding Path=TerraineImageLocation}" /> </Path.Fill >-->

I prefer the 1st option from a readability and brevity point of view.

In my explorations, I found that a guy in Russia named Rudankort created a faithful PG port called PG Forever. Looking at his directory structure, he has all of the background hexes in a single image like this:

image

There are approximately 500+ images that I would need to clip and save to the file system if I wanted to separate them all out. I thought of another way of loading the entire image and then pulling pieces out of the image at runtime. Another challenge is to get the icons – removing the back fill.  I’ll work on that coming up…

MVVM and Game Design

I fired up an old laptop last week and I found Panzer General installed on it. Bring back fond memories of wasted hours, I thought how cool it would be to port this game to Windows Phone. Ambitious? Absolutely. Realistic? Not even close. Undaunted, I thought a bit about how a game board are might be modeled. You can see from this screen shot that the game board is a series of Hexes – like so:

image

Dialing up the MVVM pattern, I thought the basic unit of code is the Hex. I looked at this article about building a MVVM Pattern for a Sudoku found in MSDN here but quickly threw it away. I think that Miller’s implementation is a bit mangled and all of the code in the View smells to me like an incorrect design. I realized that I need a HexModel, and HexView, and a HexViewModel. To start, I put all of the properties that I care about into the Model like so:

public class HexModel { public double Width { get; set; } public double Height { get; set; } public double CanvasTop { get; set; } public double CanvasLeft { get; set; } public SolidColorBrush Fill { get; set; } public int XCoordinate { get; set; } public int YCoordinate { get; set; } } }

I then put all of the implementation of INotifiedPropertyChanged in the ViewModel. It makes alot of sense to me that the View is a passive implementation of the underlying data structures (EF classes, POCOs, TakeYourPickDataStructure) and the ViewModel where all of the heavy lifting occurs (For example, your LINQ for the CRUD if this was working with a permanent data store) and that is where you notify the consuming layers that properties changed. Following this mental model, I first implemented an abstract class to handle INotifiedPropertyChanged:

public class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void NotifyPropertyChanged(String propertyName) { if (string.IsNullOrEmpty(propertyName)) throw new ArgumentException("Argument 'propertyName' cannot be null or empty."); if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } }

I then implemented the HexViewModel like this (partial snip):

public class HexViewModel: ViewModelBase { HexModel _hexModel; public HexViewModel() { _hexModel = new HexModel(); Width = 100; Height = 100; CanvasTop = 0.0; CanvasLeft = 0.0; Fill = new SolidColorBrush(Colors.Gray); XCoordinate = 0; YCoordinate = 0; } public double Width { get { return _hexModel.Width; } set { _hexModel.Width = value; NotifyPropertyChanged("Width"); } }

With this view model done, I then was onto the View itself. A hex shape is a bit more tricky than a square for a game board – heck you can use a grid if you just want to build a game board of squares. I found a pretty good implementation of a WPF hex on Stack Overflow here. Since the Panzer General Hex is flat on the top (versus the flat sides in the Stack Overflow question), I reversed the path coordinates and threw it into a User Control:

<UserControl.DataContext> <vm:HexViewModel /> </UserControl.DataContext> <Canvas x:Name="CanvasRoot" Height="100" Width="100"> <Path x:Name="PathRoot" IsHitTestVisible="True" Width="100" Height="100" d:LayoutOverrides="None" d:LastTangent="0,0" Stroke="Black" Fill="{Binding Path=Fill}" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="0" Stretch="Fill" Data="M0,8.660254 L5,17.320508 15,17.320508 20,8.660254 15,0 5,0 0,8.660254 z" Canvas.Left="{Binding Path=CanvasLeft}" Canvas.Top="{Binding Path=CanvasTop}"> </Path> <TextBox x:Name="LocationTextBox" IsHitTestVisible="False" Canvas.Left="21" Canvas.Top="22" FontSize="12" Background="{Binding Path=Fill}" HorizontalAlignment="Center" BorderThickness="0" Text="{Binding Path=Coordinates}"> </TextBox> </Canvas> </UserControl>

Sure enough, I rendered great in VS2010 at design time because of the magic of data binding and INotifyPropertyChanged:

image

I then though about how I would get a series of hexes as the game board using the same MVVM pattern. I set up a BoardModel like so:

public class BoardModel { public int NumberOfRows { get; set; } public int NumberOfColumns { get; set; } public int ZoomFactor { get; set; } }

I then implemented the model and added a collection to the ViewModel to have a collection of HexModels (screen shot, not code):

image

I then put together a BoardView that goes through the collection and renders out the HexViews in the constructor:

private void BindCanvas() { MainCanvas.Height = (_boardViewModel.NumberOfRows * 100); MainCanvas.Width = (_boardViewModel.NumberOfColumns * 100); foreach (HexViewModel hexViewModel in _boardViewModel.HexViewModels) { this.MainCanvas.Children.Add(CreateHexView(hexViewModel)); } }

Because this is not data bound, I don’t see the HexViews at design time. However, when I slap the BoardView into the MainPage at design time, I get a nice playboard:

image

Also, I used the Windows Phone Control Toolkit’s Gesture Listen class and I can intercept touches and active and deactivates hexes on a user’s touch (I love the SOC of the IPropertyNotifiedChanged):

private void GestureListener_Tap(object sender, Microsoft.Phone.Controls.GestureEventArgs e) { Path path = (Path)e.OriginalSource; Canvas canvas = (Canvas)path.Parent; HexView hexView = (HexView)canvas.Parent; if (currentHexViewModel != null) currentHexViewModel.Fill = new SolidColorBrush(Colors.Gray); currentHexViewModel = (HexViewModel)hexView.DataContext; currentHexViewModel.Fill = new SolidColorBrush(Colors.Red); e.Handled = true; }

Notice how I am changing the property of the ViewModel – not of the UI control.

I am not sure how to make the BoardVewModel can be data bound like the HexViewModel. I was thinking that the Board should expose a collection of HexViews (versus ViewModel) but that does seem right. Also, I am not sure how to code up design time data binding to that collection – I would think I would need a container control. After futzing around with Rob Seder and not getting anywhere, I gave up and went with the code-behind implementation. I am sure I will revisit, but I am jazzed just to get the board working. Up next, I want to see if I can put in a picture into the Hex.

A couple of gotchas for creating Windows Phone 7 Applications

I have been doing Nathan’s Transforms and Animations projects. I made a typo when naming a Storyboard (note the space as the 1st character) and when I tried to auto-generate the code behind function:

image

I got this error:

image

Notice how there is a space between the ‘ and the P -> however, if you are typing/reading fast, you might miss it.

Another gotcha I found was in Chapter 16, when coding up the DoubleAnimationUsingKeyFrames. I copied the code verbatim from his book and I got this:

image

When I added an x:name, the compiler was once again happy.

My final gotcha looked like this:

image

And the offending line is in the LotteryBall.Percolating Setter:

Storyboard.SetTargetProperty(this.percolatingStoryboard, new PropertyPath("Y"));

WTF? How can you set a property path to a property that doesn’t exist? I ran the code sample and it works! Ugh, I am at a loss. He has a Y property in the Tilt class that I didn’t include because it is non-essential. Oh well, it would have been cool to see the final solution work…

On Line Source Control

I have received some requests for the source code/project of the examples that I have been showing on this blog. Here to fore, I have been zipping up the solution and email the requestor. I thought I could take it up a level by using some of the services that are available on the internet. I checked out github, codeplex, googlecode, and assembla. I had 3 basic criteria – can I restrict the project to people I want, can I use TFS (I am a Microsoft dev after all) and is it free? Here is what I found:

Site

TFS?

Restrict Users

Free?

github

N

N (for free)

Y

codeplex

Y

N

Y

googlecode

N

Y

Y

assembla

N

Y

N

Rob Seder then suggested that I just upload the zipped up projects that I want into the blog using LiveWriter. <Sound of me slapping my forehead>Doh! </Sound of me slapping my forehead>.  Finally, for basic file sharing, I can always use SkyDrive…

I’ll go back and do that for the posts that people have emailed me about and perhaps in the future…