Showing posts with label graphics. Show all posts
Showing posts with label graphics. Show all posts

Sunday, August 21, 2016

It’s been a while, but I’m back.

When I started this blog, way back in 2008, my main goal was to record and share my experiences as I grew as a programmer.  I didn’t want to rehash the same information that you got from a hundred other sources.  Instead I wanted to show the unique parts of my eternal journey to be a better programmer.

During my three year hiatus I grew a lot, but I would put quite a bit of it under the heading of “Everyone else has been there, done that” and I could not really justify wasting your time reading such drivel.  Plus, lots of personal “stuff” happened, such as the death of my Mother-in-law and last years diagnosis of my wife’s cancer.

So, I expect to start adding new content soon.  Prepare yourself.  You have been warned.

Friday, November 15, 2013

Creating XAML hexagons without using a PATH element

I was recently working on a small Windows Phone 8 app that needed some tessellated (tiled) hexagons and after searching the web for a simple solution, I did not find what I wanted. 

All of the solutions involved drawing the hexagon using drawing primitives such as the PATH element.  I wanted a “simpler” solution.  I wanted a Grid based solution, and here it is.

To start with I needed to know two semi-basic points about hexagons: what is the ratio of height to width, and (with the hex “standing” on one of its flat sides) what is the the ratio between the horizontal length of the flat and the horizontal distance from the end of the flat side to the edge of the hexagon.  (Boy that is harder to write than it is to just see it)

I found both pieces of information at: Hexnet

Hexnet shows that if the length of one side of a hex has a length of 1, then the overall width is 2 and the height of the hexagon is √3 (approx. 1.73).  So, if we were to build this in a grid, we would have a single hexagon take up 6 cells (2 rows and 3 columns) and our two critical semi-basic points are: √3 to 2 and 1 to .5.

If we wanted a hexagon with an overall width of 200px (200 times our example above), our grid would be defined as:

   1:  <Grid x:Name="LayoutRoot" Background="White" Width="200" Height="173">
   2:      <Grid.ColumnDefinitions>
   3:          <ColumnDefinition Width="*"></ColumnDefinition>
   4:          <ColumnDefinition Width="2*"></ColumnDefinition>
   5:          <ColumnDefinition Width="*"></ColumnDefinition>
   6:      </Grid.ColumnDefinitions>
   7:      <Grid.RowDefinitions>
   8:          <RowDefinition></RowDefinition>
   9:          <RowDefinition></RowDefinition>
  10:      </Grid.RowDefinitions>
  11:  </Grid>



if you look at this grid with gridlines on, you get:


image



Now from here we can either create a outline of a hexagon using 6 line objects, create a solid hexagon using 3 rectangle objects or combine both for an outlined solid color hexagon.


“Wait!”, you say.  “I can see the six lines making up the outline of a hexagon, but how could you do a solid color hexagon with three squares?”


Well, I’m glad you asked.  If you fill the center column with a rectangle, that’s one.  If you make a copy but rotate it 60 degrees using the center as the pivot point and make a third and rotate it –60 degrees then you have your solid color hexagon.


And, if you just start repeating the second and third columns when you add columns, you can start to create a grid full of tessellated (tiled) hexagons, just like I did in this image.


image


And here is the complete xaml I used to create the above image (From this code it was simple to extract rules that I used to create large fields of tessellated (tiled) hexagons in code):


   1:  <UserControl x:Class="Hexagon.MainPage"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   5:      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   6:      mc:Ignorable="d"
   7:      d:DesignHeight="346" d:DesignWidth="500">
   8:      <Viewbox Stretch="Uniform">
   9:          <Grid x:Name="LayoutRoot" Background="White" Width="500" Height="346" Margin="50">
  10:              <Grid.ColumnDefinitions>
  11:                  <ColumnDefinition Width="*"></ColumnDefinition>
  12:                  <ColumnDefinition Width="2*"></ColumnDefinition>
  13:                  <ColumnDefinition Width="*"></ColumnDefinition>
  14:                  <ColumnDefinition Width="2*"></ColumnDefinition>
  15:                  <ColumnDefinition Width="*"></ColumnDefinition>
  16:                  <ColumnDefinition Width="2*"></ColumnDefinition>
  17:                  <ColumnDefinition Width="*"></ColumnDefinition>
  18:              </Grid.ColumnDefinitions>
  19:              <Grid.RowDefinitions>
  20:                  <RowDefinition></RowDefinition>
  21:                  <RowDefinition></RowDefinition>
  22:                  <RowDefinition></RowDefinition>
  23:                  <RowDefinition></RowDefinition>
  24:              </Grid.RowDefinitions>
  25:              <Rectangle Fill="Pink" RenderTransformOrigin="0.5, 0.5" Grid.Column="1" Grid.RowSpan="2"></Rectangle>
  26:              <Rectangle Fill="Pink" RenderTransformOrigin="0.5, 0.5" Grid.Column="1" Grid.RowSpan="2">
  27:                  <Rectangle.RenderTransform>
  28:                      <RotateTransform Angle="-60" />
  29:                  </Rectangle.RenderTransform>
  30:              </Rectangle>
  31:              <Rectangle Fill="Pink" RenderTransformOrigin="0.5, 0.5" Grid.Column="1" Grid.RowSpan="2">
  32:                  <Rectangle.RenderTransform>
  33:                      <RotateTransform Angle="60" />
  34:                  </Rectangle.RenderTransform>
  35:              </Rectangle>
  36:              <Rectangle Fill="Aquamarine" RenderTransformOrigin="0.5, 0.5" Grid.Column="3" Grid.Row="1" Grid.RowSpan="2"></Rectangle>
  37:              <Rectangle Fill="Aquamarine" RenderTransformOrigin="0.5, 0.5" Grid.Column="3" Grid.Row="1" Grid.RowSpan="2">
  38:                  <Rectangle.RenderTransform>
  39:                      <RotateTransform Angle="-60" />
  40:                  </Rectangle.RenderTransform>
  41:              </Rectangle>
  42:              <Rectangle Fill="Aquamarine" RenderTransformOrigin="0.5, 0.5" Grid.Column="3" Grid.Row="1" Grid.RowSpan="2">
  43:                  <Rectangle.RenderTransform>
  44:                      <RotateTransform Angle="60" />
  45:                  </Rectangle.RenderTransform>
  46:              </Rectangle>
  47:              <Rectangle Fill="LightBlue" RenderTransformOrigin="0.5, 0.5" Grid.Column="5" Grid.Row="2" Grid.RowSpan="2"></Rectangle>
  48:              <Rectangle Fill="LightBlue" RenderTransformOrigin="0.5, 0.5" Grid.Column="5" Grid.Row="2" Grid.RowSpan="2">
  49:                  <Rectangle.RenderTransform>
  50:                      <RotateTransform Angle="-60" />
  51:                  </Rectangle.RenderTransform>
  52:              </Rectangle>
  53:              <Rectangle Fill="LightBlue" RenderTransformOrigin="0.5, 0.5" Grid.Column="5" Grid.Row="2" Grid.RowSpan="2">
  54:                  <Rectangle.RenderTransform>
  55:                      <RotateTransform Angle="60" />
  56:                  </Rectangle.RenderTransform>
  57:              </Rectangle>
  58:              <Rectangle Fill="LemonChiffon" RenderTransformOrigin="0.5, 0.5" Grid.Column="1" Grid.Row="2" Grid.RowSpan="2"></Rectangle>
  59:              <Rectangle Fill="LemonChiffon" RenderTransformOrigin="0.5, 0.5" Grid.Column="1" Grid.Row="2" Grid.RowSpan="2">
  60:                  <Rectangle.RenderTransform>
  61:                      <RotateTransform Angle="-60" />
  62:                  </Rectangle.RenderTransform>
  63:              </Rectangle>
  64:              <Rectangle Fill="LemonChiffon" RenderTransformOrigin="0.5, 0.5" Grid.Column="1" Grid.Row="2" Grid.RowSpan="2">
  65:                  <Rectangle.RenderTransform>
  66:                      <RotateTransform Angle="60" />
  67:                  </Rectangle.RenderTransform>
  68:              </Rectangle>
  69:              <Rectangle Fill="Thistle" RenderTransformOrigin="0.5, 0.5" Grid.Column="5" Grid.Row="0" Grid.RowSpan="2"></Rectangle>
  70:              <Rectangle Fill="Thistle" RenderTransformOrigin="0.5, 0.5" Grid.Column="5" Grid.Row="0" Grid.RowSpan="2">
  71:                  <Rectangle.RenderTransform>
  72:                      <RotateTransform Angle="-60" />
  73:                  </Rectangle.RenderTransform>
  74:              </Rectangle>
  75:              <Rectangle Fill="Thistle" RenderTransformOrigin="0.5, 0.5" Grid.Column="5" Grid.Row="0" Grid.RowSpan="2">
  76:                  <Rectangle.RenderTransform>
  77:                      <RotateTransform Angle="60" />
  78:                  </Rectangle.RenderTransform>
  79:              </Rectangle>
  80:              <Line Grid.Column="0" Grid.Row="0" X1="1" Y1="0" X2="0" Y2="1" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  81:              <Line Grid.Column="1" Grid.Row="0" X1="1" Y1="0" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" VerticalAlignment="Top" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  82:              <Line Grid.Column="2" Grid.Row="0" X1="1" Y1="1" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  83:              <Line Grid.Column="0" Grid.Row="1" X1="1" Y1="1" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  84:              <Line Grid.Column="1" Grid.Row="1" X1="1" Y1="0" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" VerticalAlignment="Bottom" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  85:              <Line Grid.Column="2" Grid.Row="1" X1="1" Y1="0" X2="0" Y2="1" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  86:              <Line Grid.Column="3" Grid.Row="1" X1="1" Y1="0" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" VerticalAlignment="Top" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  87:              <Line Grid.Column="4" Grid.Row="0" X1="1" Y1="0" X2="0" Y2="1" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  88:              <Line Grid.Column="5" Grid.Row="0" X1="1" Y1="0" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" VerticalAlignment="Top" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  89:              <Line Grid.Column="6" Grid.Row="0" X1="1" Y1="1" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  90:              <Line Grid.Column="4" Grid.Row="1" X1="1" Y1="1" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  91:              <Line Grid.Column="5" Grid.Row="1" X1="1" Y1="0" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" VerticalAlignment="Bottom" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  92:              <Line Grid.Column="6" Grid.Row="1" X1="1" Y1="0" X2="0" Y2="1" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  93:              <Line Grid.Column="0" Grid.Row="2" X1="1" Y1="0" X2="0" Y2="1" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  94:              <Line Grid.Column="2" Grid.Row="2" X1="1" Y1="1" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  95:              <Line Grid.Column="0" Grid.Row="3" X1="1" Y1="1" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  96:              <Line Grid.Column="1" Grid.Row="3" X1="1" Y1="0" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" VerticalAlignment="Bottom" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  97:              <Line Grid.Column="2" Grid.Row="3" X1="1" Y1="0" X2="0" Y2="1" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  98:              <Line Grid.Column="3" Grid.Row="3" X1="1" Y1="0" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" VerticalAlignment="Top" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  99:              <Line Grid.Column="4" Grid.Row="2" X1="1" Y1="0" X2="0" Y2="1" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
 100:              <Line Grid.Column="6" Grid.Row="2" X1="1" Y1="1" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
 101:              <Line Grid.Column="4" Grid.Row="3" X1="1" Y1="1" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
 102:              <Line Grid.Column="5" Grid.Row="3" X1="1" Y1="0" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" VerticalAlignment="Bottom" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
 103:              <Line Grid.Column="6" Grid.Row="3" X1="1" Y1="0" X2="0" Y2="1" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
 104:          </Grid>
 105:      </Viewbox>
 106:  </UserControl>

Thursday, April 7, 2011

Random Numbers on a Bell Curve in C#

Have you ever wanted to generate some random numbers but with a distribution pattern other than the normal even pattern?

Well in a recent project I did.  I wanted a weighted distribution pattern that looked like a bell curve. 

I looked around yes there were some answers, but most got a lot deeper into math than I wanted or needed.  I simply wanted to generate a number with the probability that it was in the center of the range be greater than the probability that it was on the edges – in other words, it fit on a Bell Curve.

I cannot credit what post where gave me the simple solution, or I would post a link.  Here is a simple Extension Method to the Random class. 

It takes two parameters:

  1. Steps: How many numbers in each “Chunk” do you want.  If you want a number between 0 & 600 and set the Steps equal to 50, then the midpoint value (300) will be much more likely than 0 or 300.
  2. MaxValue: The possible range for the random number will be 0 to MaxValue - 1.

Now keep in mind that this is not mathematically perfect.  If you do not choose a step value that is a divisor of your MaxValue then you will not get the full range.  But, having said that, this is a great “Quick & Dirty” way to get a good approximation of a random bell curve.

Code Snippet
  1. public static class RandomExtender
  2. {
  3.     public static int NormalNext(this Random rnd, int Steps, int MaxValue)
  4.     {
  5.         int count = 0;
  6.         int val = 0;
  7.  
  8.         if (Steps < 1) return 0;
  9.  
  10.         while (++count * Steps <= MaxValue) val += rnd.Next(Steps);
  11.  
  12.         return val;
  13.     }
  14. }

Here is a picture of 20,000 random numbers with Steps = 50 & MaxValue = 600.

BellCurve

Monday, November 30, 2009

How to create your own Silverlight Cube Control similar to Telerik's Cube

Well, as part of our lastest project, I needed to figure a way how to spin a widget.

So, how do you rotate widgets in an Asp.Net application? Well that question is fairly simple to answer - Silverlight!

The next question is much harder: Buy or Build?

After some searches I found two controls for sale: The Telerik Cube control and the ComponentOne C1Cube control.

So, first stop, download each and put them through the paces. I found a deal breaker for each control.

We need to be able to minimize our widgets but both controls had problems with that:
- The Telerik control would resize but would apply a transform to 'Squash' the widget and according to the Telerik website FAQ, this behaviour can not be overridden.
- The Component one control 'Must' be in the shape of a cube. Ok, I know that sounds like a 'duh' point, but like I said, we need to minimize our widgets. But when I altered the height of the control to just the height of the header, the cubes' width was also adjusted.

So, that left build - but there did not seem to be any examples of how to rotate a cube from side to side, on demand. I did find an example of a continuously spinning cube. With this example to decompose, I slowly taught myself the parameters and transformations that were needed to create the appearance of a cube spinning from one face to another.

So, here is my demo of a rotating six sided cube. If you do not need to rotate to the top and the bottom, you can have sides of any height, as long as all the sides are of the same height. Plus, my example is not limited to four or six sides. This example allows for an unlimited number of sides!

Now, there a couple of things that the professional controls do that my demo cannot. My code cannot free rotate and it also does not twist as it rotates to properly show a side. If you need these properties, you may want to go with the professional controls.

Here is the Visual Studio 2008/Silverlight 3 project.

Wednesday, August 19, 2009

The easiest way to convert vector graphics to XAML

Wow, this is sooooo simple. I've been converting WMF, EMF, CDX, CDR, AI, EPS, PDF and SVG to XAML all day. This is actually fun!

First get a vector graphic and a program that can open it. I've used Adobe Illustrator, CorelDraw, Inkscape and even Windows Picture and Fax Viewer.

Next get a vector graphic that your program can open.

Finally get the Microsoft XPS printer driver if it is not already installed.

Now open the file in your program of choice and print it to the XPS printer driver.

Now rename the resultant XPS file to ZIP.

Open the ZIP file and inside the Documents/1/Pages folder is a file named 1.fpage.

Extract the file and rename it to .XAML.

Now you have a XAML file that will open in Internet Explorer, but if you want to have a XAML file that will open kaxaml or for use in a WPF or a Silverlight application, open the file in notepad and replace the FixedPage tag with the Canvas tag.

Here is a XAML graphic I converted from my Corel Gallery. (Either open link in IE7/IE8 or download the XAML file and open it in your XAML viewer of choice)

Older posts from other blogs tell you to use Inkscape and print to PDF and then open the PDF in Adobe PDF Viewer. Fron the Adobe PDF Viewer you can then print to the XPS printer driver. The reason was that Inkscape converted the file to a raster image. Then and only then could you retrieve the 1.fpage file. This was because Inkscape sent the graphic to the printer as a raster image, not a vector image.

Well, since those blog posts were written, there have been updates to Inkscape, and now Inkscape prints a vector image as a vector, so there is now no need for converting it to an intermediate format (PDF).

Sunday, March 1, 2009

Sudoku in Silverlight

You know how it is, you learn a skill (Programming in my case) because you see the results of others. Looking at the fruits of their labors, you say, "wow! I want to be able to do that." So you start learning, take some classes, read some books, maybe like me you enroll in the Computer Science program. If you get good, you may even get a job using your new found skill. But, did the learning of this skill lead you to the place you wanted to be, all those long years ago?

When I first started programming, it was to automate the games I was already playing. Then I moved into trying to replicate the types of games that I had played before on the computer. But as it usually happens, the real world got in the way. Instead of learning how to animate sprites, I had to learn how to connect to a database. Instead of graphics, I had to learn about algorithms.

Now, I'm not knocking my job and the work I do. I rarely go to "Work" since I get to indulge in my passion for programming every day. But, I feel this need to give more than lip service to the original reason that I got into the "Biz" in the first place.

Believe me, in 25 years the landscape of game programming has changed so much that I hardly recognize it! And, I've tried to keep my feet wet in the technology the entire time. The time when the ability to draw a digital stick figure and write a great game around it (Lode Runner) is long gone. When I first made up my mind to bite the bullet and truly try to learn game programming, in depth, the technology seemed so complicated that I almost quit.

But Rome was not built in a day and neither were any of my favorite games. Like I tell all of my junior programmers, "Start at the beginning, then just program each little piece until it is done”, I decided to start with the smallest bite I could “chew” and then just keep going. The first "Game" I wrote was a Sudoku generator with a Javascript back end. I Wrapped a website around it and launched it a couple of years ago.

Well, here is the thing about games, the are not all created equal. Sure Sudoku is a "Game" per se, but not really related to my true love, boardgames. So http://www.freedailygames.net languished for a few years without any upkeep. I even thought of just shutting it down, but according to Google analytics, I have a few very loyal customers. And, now with the new technology of Silverlight, I thought that updating my site with a Silverlight implementation of Sudoku would help me build my skills and give freedailygames.net a tech boost.

Well, here is my take on Sudoku in Silverlight.



And here is a zip file containing the entire Visual Studio 2008 Project.

Sunday, February 8, 2009

Fancy fonts in XAML (Silverlight and WPF)

Outline, Shadow, Embossed and Engraved text in XAML.

Here are a few quick and easy ways to spice of your text in XAML.

To use these tricks, you need to have the ability to exactly place the text. For that you need to use a Canvas Layout control.


<UserControl x:Class="SilverlightApplication2.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="320" Height="200">
<Grid x:Name="LayoutRoot" Background="White">
<Grid.ColumnDefinitions >
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Button Margin="3">
<Canvas Width="800" Height="60">
<TextBlock FontSize="36"
Canvas.Top="2" Canvas.Left="10">Outline</TextBlock>
<TextBlock FontSize="36"
Canvas.Top="2" Canvas.Left="12">Outline</TextBlock>
<TextBlock FontSize="36"
Canvas.Top="4" Canvas.Left="10">Outline</TextBlock>
<TextBlock FontSize="36"
Canvas.Top="4" Canvas.Left="12">Outline</TextBlock>
<TextBlock FontSize="36"
Canvas.Top="3" Canvas.Left="11"
Foreground="White">Outline</TextBlock>
</Canvas>
</Button>
<Button Grid.Column="0" Grid.Row="1" Margin="3">
<Canvas Width="800" Height="60">
<TextBlock FontSize="36"
Canvas.Top="5" Canvas.Left="4"
Foreground="Black">Shadow</TextBlock>
<TextBlock FontSize="36"
Canvas.Top="3" Canvas.Left="2"
Foreground="Gray">Shadow</TextBlock>
</Canvas>
</Button>
<Button Grid.Column="1" Grid.Row="0" Margin="3">
<Canvas Width="800" Height="60">
<TextBlock FontSize="36"
Canvas.Top="3" Canvas.Left="0"
Foreground="Gray">Engrave</TextBlock>
<TextBlock FontSize="36"
Canvas.Top="5" Canvas.Left="2"
Foreground="White">Engrave</TextBlock>
<TextBlock FontSize="36"
Canvas.Top="4" Canvas.Left="1"
Foreground="Black">Engrave</TextBlock>
</Canvas>
</Button>
<Button Grid.Column="1" Grid.Row="1" Margin="3">
<Canvas Width="800" Height="60">
<TextBlock FontSize="36"
Canvas.Top="5" Canvas.Left="3"
Foreground="Gray">Emboss</TextBlock>
<TextBlock FontSize="36"
Canvas.Top="2" Canvas.Left="2"
Foreground="White">Emboss</TextBlock>
<TextBlock FontSize="36"
Canvas.Top="4" Canvas.Left="4"
Foreground="Black">Emboss</TextBlock>
</Canvas>
</Button>
</Grid>
</UserControl>

Saturday, December 6, 2008

Switcheroo - An old "Magazine" game in Silverlight, Part 1

In a previous post, I described the game Switcheroo. In this first post we will create a the start splash screen, the game board and the end splash screen.

The splash screen for a very visual game should be striking. Unfortunately, I am not a graphic artist, so the best I could come up with is the name switcheroo rendered using WordArt in Word.

We need to display the splash screen along with a start button. When the start button is clicked, we can then hide the splash screen and display the game board.

If you remember from that previous post, players can either place a piece on the 5x5 gameboard or rotate a row to the left or right, or rotate a column up or down. So, not only do we need a 5x5 checkerboard but we also need a mechanism to allow the players to rotate the columns and rows.

To accomplish this I added an extra column at the start and the end of the gameboard and an extra row to the top and the bottom of the gameboard. I added small golden arrows.

Once the game has been one or lost, on the placement of the winning move, we need to hide the gameboard and show the splash screen again. This time with a button declaring their win or loss and asking if they wish to play again.

The only problem with this is from a aesthetic point. When we place the winning piece, rotate the winning piece into place or our opponents move defeats us, we do not want to immediately flash to the splash page. The player may not have been able to even see that the move was a winning move or what their opponent even did to win, so we need to include a delay upon determining that the game is in fact over.

In the code for this first part, the both splash screens will work as they are supposed to, but since we have not created any game logic yet, we will consider any click on the gameboard to be the winning move and after our short delay the "You lose" version of the splash screen will display.

If you click on the button, the gameboard will display again and the whole process will start again, but the code will rotate between the "You win" and "You lose" messages to verify that both code paths work.

As you can see, instead of a lot of repetitive XAML, the only hardcoded XAML sets up the grid that will contain the gameboard and the elements for the splash screen.
<UserControl x:Class="Switcheroo.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="300" Height="300">
<Grid x:Name="LayoutRoot"
Tag="Bob">
<Grid.ColumnDefinitions>
<ColumnDefinition Width='25' />
<ColumnDefinition Width='50' />
<ColumnDefinition Width='50' />
<ColumnDefinition Width='50' />
<ColumnDefinition Width='50' />
<ColumnDefinition Width='50' />
<ColumnDefinition Width='25' />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height='25' />
<RowDefinition Height='50' />
<RowDefinition Height='50' />
<RowDefinition Height='50' />
<RowDefinition Height='50' />
<RowDefinition Height='50' />
<RowDefinition Height='25' />
</Grid.RowDefinitions>
<Image x:Name="XamlBoard"
Grid.RowSpan='7'
Grid.ColumnSpan='7'
Source='Splash.png' />
<StackPanel VerticalAlignment="Center"
HorizontalAlignment="Center"
Grid.RowSpan="7"
Grid.ColumnSpan="7">
<Button Click="Start_Click"
Content="Start"></Button>
</StackPanel>
</Grid>
</UserControl>

The XAML code also available here!

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;

namespace Switcheroo
{
public partial class Page
{
private class MyColors
{
public static Color Aliceblue { get { return GenerateColorStruct(0xFFF0F8FF); } }
public static Color Cyan { get { return GenerateColorStruct(0xFF00FFFF); } }
public static Color Darkcyan { get { return GenerateColorStruct(0xFF008B8B); } }
public static Color Darkgoldenrod { get { return GenerateColorStruct(0xFFB8860B); } }
public static Color Gold { get { return GenerateColorStruct(0xFFFFD700); } }

private static Color GenerateColorStruct(uint color)
{
var mask = 0x000000FF;

var returnval = new Color
{
A = ((byte)((color >> 24) & mask)),
R = ((byte)((color >> 16) & mask)),
G = ((byte)((color >> 8) & mask)),
B = ((byte)((color) & mask))
};

return returnval;
}
}

private class GameBoard
{
internal readonly int[,] Board = new int[5, 5];

public GameBoard()
{
for (var i = 0; i < 5; i++)
{
for (var j = 0; j < 5; j++)
{
Board[i, j] = -1;
}
}
}
}

readonly DispatcherTimer dt = new DispatcherTimer();

private bool GameOver;
private int GameWinner = -1;
private GameBoard Switcheroo = new GameBoard();

public Page()
{
InitializeComponent();
}

private void Start_Click(object sender, RoutedEventArgs e)
{
Switcheroo = new GameBoard();
LayoutRoot.Children.Clear();
CreateGameBoard();
GameOver = false;
}

private void dt_Tick(object sender, EventArgs e)
{
dt.Stop();
DisplayGameOverSplash();
}

private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
GameOver = true;

GameWinner = GameWinner == 0 ? 1 : 0;

if (GameOver)
{
dt.Interval = new TimeSpan(0, 0, 0, 0, 500); // 500 Milliseconds
dt.Tick += dt_Tick;
dt.Start();
}
}

private void CreateGameBoard()
{
var CellColor = false;
Canvas c;

for (var i = 0; i < 7; i++)
{
for (var j = 0; j < 7; j++)
{
if (i != 0 && i != 6 && j != 0 && j != 6)
{
c = new Canvas
{
Tag = (i + ":" + j),
Background =
(CellColor
? new SolidColorBrush(MyColors.Aliceblue)
: new SolidColorBrush(MyColors.Darkcyan))
};
}
else
{
c = new Canvas
{
Tag = (i + ":" + j),
Background = new SolidColorBrush(MyColors.Cyan)
};


if (j == 6 && i != 0 && i != 6)
{
var p = new Polygon
{
Fill = new SolidColorBrush(MyColors.Gold),
Stroke = new SolidColorBrush(MyColors.Darkgoldenrod)
};

p.Points.Add(new Point(10, 5));
p.Points.Add(new Point(40, 5));
p.Points.Add(new Point(25, 15));

c.Children.Add(p);
}
else if (j == 0 && i != 0 && i != 6)
{
var p = new Polygon
{
Fill = new SolidColorBrush(MyColors.Gold),
Stroke = new SolidColorBrush(MyColors.Darkgoldenrod)
};

p.Points.Add(new Point(10, 20));
p.Points.Add(new Point(40, 20));
p.Points.Add(new Point(25, 10));

c.Children.Add(p);

}
else if (i == 6 && j != 0 && j != 6)
{
var p = new Polygon
{
Fill = new SolidColorBrush(MyColors.Gold),
Stroke = new SolidColorBrush(MyColors.Darkgoldenrod)
};

p.Points.Add(new Point(5, 10));
p.Points.Add(new Point(5, 40));
p.Points.Add(new Point(15, 25));

c.Children.Add(p);

}
else if (i == 0 && j != 0 && j != 6)
{
var p = new Polygon
{
Fill = new SolidColorBrush(MyColors.Gold),
Stroke = new SolidColorBrush(MyColors.Darkgoldenrod)
};

p.Points.Add(new Point(20, 10));
p.Points.Add(new Point(20, 40));
p.Points.Add(new Point(10, 25));

c.Children.Add(p);

}
}

c.MouseLeftButtonDown += Canvas_MouseLeftButtonDown;
Grid.SetColumn(c, i);
Grid.SetRow(c, j);

LayoutRoot.Children.Add(c);

CellColor = !CellColor;
}
}
}

private void DisplayGameOverSplash()
{
string ButtonText = GameWinner != 0 ? "You Win! Play Again?" : "You Loose! Play Again?";

const string Splash = "<Image xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' " +
"xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' " +
"x:Name='XamlBoard' Grid.RowSpan='7' Grid.ColumnSpan='7' Source='Splash.png' /> ";

const string Stack = "<StackPanel xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' " +
"xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' VerticalAlignment='Center' " +
"HorizontalAlignment='Center' Grid.RowSpan='7' Grid.ColumnSpan='7' />";

string Button = "<Button xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' " +
"xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' Content='" + ButtonText + "'></Button>";

LayoutRoot.Children.Clear();
LayoutRoot.Children.Add((Image)XamlReader.Load(Splash));
LayoutRoot.Children.Add((StackPanel)XamlReader.Load(Stack));
((StackPanel)LayoutRoot.Children[1]).Children.Add((Button)XamlReader.Load(Button));
((Button)((StackPanel)LayoutRoot.Children[1]).Children[0]).Click += Start_Click;
}
}
}

The C# code also available here!

Thursday, October 23, 2008

Where have all the colors gone?

If you are programming in Silverlight, you probably have seen all the colors available in the XAML. Want a line that is AliceBlue, no problem, but if you want to create the same line programatically in C#, good luck.

The Colors class is just a small subset of all the HTML named colors, so the only way to add AliceBlue is to use Color.FromArgb, so you go out to a website that lists all the colors' RGB values. AliceBlue is #F0F8FF, so you paste the RGB value into Color.FromArgb.

But wait! Color.FromArgb expects 4 bytes, not a single number (The first byte is the Alpha value - i.e. the transparentcy of the color, hence the A in front of the RGB) and our color is only 3 bytes. We can add #FF in front of #F0F8FF but Color.FromArgb expects them to be separate values separated by a comma.

Color.FromArgb(#FF, #F0, #F8, #FF) will not work because C# does not recognize them as numbers. The key to having C# recognize them as hex numbers is to replace the hash(#) with 0x. So AliceBlue is now Color.FromArgb(0xFF, 0xF0, 0xF8, 0xFF).

Too much work you say? I agree. I just want to use my named colors. So, I created my own Color class that in Silverlight will output named colors!

The code looks like this:


private class MyColors
{
    public static Color Aliceblue
    {
        get
        {
            return GenerateColorStruct(0xFFF0F8FF);
        }
    }

    .
    .
    .

    private static Color GenerateColorStruct
                                    (uint color)
    {
        var mask = 0x000000FF;
        var returnval = new Color
        {
            A = ((byte)((color >> 24) & mask)),
            R = ((byte)((color >> 16) & mask)),
            G = ((byte)((color >> 8) & mask)),
            B = ((byte)((color) & mask))
        };

        return returnval;
    }
}


The entire .cs file can be found here. Since this is a private class that you can add to any Silverlight page.cs file that needs named colors , you can also add your own named colors that you want to use all the time!

You can also compile it to its own dll, but I like it better in the page.cs file. For size of outputs sake, right before you publish you can remove all the unused colors.

Wednesday, October 22, 2008

Old Games In Silverlight

It continually amazes me how many great games were published in the 80's and 90's in hobby programming magazines. Sure, they are not the type of games that a company like Electronic Arts would be interested in and are definitely not up to the standards of today's games on the PC, but casual gaming on the web and mobile devices has possibly created a new home for these games.

In one of my old programming magazines (Compute's! Gazette, June 1986 - Issue 36, Vol. 4, No. 6) described a game called Switcheroo, by Kevin Mykytyn and Mark Tuttle. A simple connect 5 on a 5x5 grid (horizontally or vertically, no diagonals) with a twist. The player can place a piece of his color on an empty space or he may shift a column up or down, or shift a row left or right. Any pieces that are removed from the board by the shift are places in at the end of the column or row in the newly vacant space.

I think that this game would be a great coding test for Silverlight, so I will be posting a multi-part blog entry on writing Switcheroo in Silverlight2.0.

Unfortunately, all the games published in Compute's! Gazette were in a version of Machine Language called "MLX", so the effort would be heroic to try to adjust the code to a more modern platform such as Silverlight. And, with the fact that there was no AI (It was a 2 player game) and we will want to play against the computer, we will be starting from scratch.