Skip to content

Instantly share code, notes, and snippets.

@mikezila
Last active January 5, 2017 06:18
Show Gist options
  • Save mikezila/162efb64a52a3771c0e6a332bc75159a to your computer and use it in GitHub Desktop.
Save mikezila/162efb64a52a3771c0e6a332bc75159a to your computer and use it in GitHub Desktop.
The Game of Life in C#. Nothing special. Wanted to try rendering something using GDI+ in winforms as simply as possible while still being fast. Abusing a Timer in this way is kind of nasty, but that coupled with unsafe bitmap drawing is actually pretty performant for simple things.
using System;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Windows.Forms;
namespace chip8
{
public partial class MainForm : Form
{
Graphics g;
Stopwatch watch = new Stopwatch();
long logicTime = 0;
long drawTime = 0;
const int cellsWidth = 400;
const int cellsHeight = 300;
int[] cells = new int[cellsWidth * cellsHeight];
static Random rand = new Random();
Bitmap raster = new Bitmap(cellsWidth, cellsHeight, PixelFormat.Format24bppRgb);
public MainForm()
{
InitializeComponent();
this.ClientSize = new Size(cellsWidth*2,cellsHeight*2);
drawTimer.Interval = 1;
drawTimer.Tick += (TimerTick);
drawTimer.Start();
g = this.CreateGraphics();
g.SmoothingMode = SmoothingMode.None;
g.InterpolationMode = InterpolationMode.NearestNeighbor;
g.PixelOffsetMode = PixelOffsetMode.Half;
g.Clear(Color.White);
Seed();
}
void Seed()
{
for (int i = 0; i < cells.Length; i++)
cells[i] = rand.Next(0, 2);
}
void UpdateCells()
{
var newCells = (int[])cells.Clone();
for (int i = 0; i < cells.Length; i++) {
int liveNeighbors = 0;
liveNeighbors += cells.WrappedLookup(i - 1); //left
liveNeighbors += cells.WrappedLookup(i + 1); //right
liveNeighbors += cells.WrappedLookup(i + cellsWidth);//bottom
liveNeighbors += cells.WrappedLookup(i - cellsWidth);//top
liveNeighbors += cells.WrappedLookup(i - 1 - cellsWidth);//top-left
liveNeighbors += cells.WrappedLookup(i + 1 - cellsWidth);//top-right
liveNeighbors += cells.WrappedLookup(i - 1 + cellsWidth);//bottom-left
liveNeighbors += cells.WrappedLookup(i + 1 + cellsWidth);//bottom-right
if (cells[i] == 0 && liveNeighbors == 3) {
newCells[i] = 1;
continue;
}
if (liveNeighbors < 2)
newCells[i] = 0;
else if (liveNeighbors == 2 || liveNeighbors == 3)
continue;
else if (liveNeighbors > 3)
newCells[i] = 0;
}
cells = newCells;
}
int x = 0;
int y = 0;
void SafeDraw()
{
for (int i = 0; i < cells.Length; i++) {
if (cells[i] == 0) {
raster.SetPixel(x++, y, Color.White);
} else if (cells[i] == 1) {
raster.SetPixel(x++, y, Color.Black);
}
if (x == cellsWidth) {
y++;
x = 0;
}
if (y == cellsHeight)
x = y = 0;
}
g.DrawImage(raster, 0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height);
}
unsafe void FastDraw()
{
BitmapData bitmapData = raster.LockBits(new Rectangle(0, 0, raster.Width, raster.Height), ImageLockMode.ReadWrite, raster.PixelFormat);
var ptrFirstPixel = (byte*)bitmapData.Scan0;
int ptrSeek = 0;
for (int i = 0; i < cells.Length; i++) {
if (cells[i] == 1) {
ptrFirstPixel[ptrSeek++] = (byte)0;
ptrFirstPixel[ptrSeek++] = (byte)0;
ptrFirstPixel[ptrSeek++] = (byte)0;
} else {
ptrFirstPixel[ptrSeek++] = (byte)255;
ptrFirstPixel[ptrSeek++] = (byte)255;
ptrFirstPixel[ptrSeek++] = (byte)255;
}
}
raster.UnlockBits(bitmapData);
g.DrawImage(raster, 0, 0, this.ClientRectangle.Width, this.ClientRectangle.Height);
}
bool fastDrawing = true;
void TimerTick(object sender, EventArgs e)
{
watch.Start();
UpdateCells();
logicTime = watch.ElapsedMilliseconds;
watch.Restart();
if (fastDrawing)
FastDraw();
else
SafeDraw();
drawTime = watch.ElapsedMilliseconds;
if (fastDrawing)
Text = "Life - " + logicTime.ToString() + "ms logic, " + drawTime + "ms draw" + " (unsafe)";
else
Text = "Life - " + logicTime.ToString() + "ms logic, " + drawTime + "ms draw" + " (safe)";
watch.Reset();
}
void Clicked(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
Seed();
else if (e.Button == MouseButtons.Right)
fastDrawing = !fastDrawing;
}
}
public static class Util
{
public static int WrappedLookup(this int[] array, int index)
{
if (index < 0) {
return array[(array.Length - 1) + (index % (array.Length - 1))];
} else
return array[index % (array.Length - 1)];
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment