PuppyBox
Tags: csharp, gamedev, showdev
Published: 2022-11-13 15:14:09

## Introduction

[Github repo](https://github.com/KDevZilla/PuppyBox)
[Download Exe](https://github.com/KDevZilla/PuppyBox/releases/download/v1.0.0/PuppyBox.zip)

![Game animation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t4ju484njmsksb7dbakg.gif)

This is a clone go Gecko Shuffle game.


## Rule of this game
	
1. There are 3 boxes, 2 of which are blank boxes, and one of which 
features a picture of a puppy. 
2. Three of them will move at random. 
3. You choose the one you believe it is a puppy box. 
4. After you have choosen the box, program will reveal which box is a puppy.
5. The more choices you guess correctly, the more points you receive.


**The Code**
This game is so small. So almost all of the logics are in the same place as UI.
	
	
**The UI**
  Form1 contains the logic to shuffle and move the boxes, 
 the control we use for rendering the boxes is picturebox.
	
**The Game**
  Game class keeps the score, box moving speed, current level, game state information.

	
**Box class**
  This class reponsible for drawing itself.
  There are 5 states of the box.

**Drawing animation**
  The logic behind drawing an animation is in Form1.OpenBox() method.
  Program will loop though all of the BoxStateEnum
  then set a boxstate to a specific box object then call RefreshUI() method.

```C#
private void OpenBox(int BoxIndex) {
  var values = Enum.GetValues(typeof (Box.BoxStateEnum));
  foreach(Box.BoxStateEnum state in values) {
    boxs[BoxIndex].BoxState = state;
    RefreshUI();
    Wait(MiliSecondAnimationWaitEachFrame);

  }

}

public enum BoxStateEnum {
  Close,
  BegintoOpen1,
  BegintoOpen2,
  BegintoOpen3,
  Open
}
```


The logic behind drawing method is using Graphics.FillPolygon() method.
We have DrawClose(), 
DrawBeginToOpen1(), 
DrawBeginToOpen2(), 
DrawBeginToOpen3(), 
DrawOpen()

These method follow the same logic
1. Create a list of Point
2. using FillPolyGon method to draw each part of the picture.
	For DrawClose() and DrowOpen, it is quite simple but for DrawBegingToOpen1(), DrawBegingToOpen2(), DrawBegingToOpen3()
	They are quite complicated.
	

```C#
	private void DrawClose(Graphics g) {
	  Rectangle FillArea = new Rectangle(new Point(Left, Top), new Size(Width - 2, Height - 2));
	  g.FillRectangle(BackBrush, FillArea);
	  List < Point > listPoint = new List < Point > ();

	  Point LeftSideTopLeft = new Point(TopLeftPoint.X - this.BoxLength, TopLeftPoint.Y + this.BoxLength);
	  Point LeftSideBottomLeft = new Point(LeftSideTopLeft.X, LeftSideTopLeft.Y + this.Height);

	  listPoint.Add(TopLeftPoint);
	  listPoint.Add(LeftSideTopLeft);
	  listPoint.Add(LeftSideBottomLeft);
	  listPoint.Add(BottomLeftPoint);

	  g.FillPolygon(BackBrush, listPoint.ToArray());
	  listPoint.Clear();
	  Point DownSideBottomLeft = LeftSideBottomLeft;
	  Point DownSideBottomRight = new Point(LeftSideBottomLeft.X + this.Width, DownSideBottomLeft.Y);
	  Point DownSideTopRight = BottomRightPoint;

	  listPoint.Add(BottomLeftPoint);
	  listPoint.Add(DownSideBottomLeft);
	  listPoint.Add(DownSideBottomRight);
	  listPoint.Add(DownSideTopRight);

	  g.FillPolygon(BackBrush, listPoint.ToArray());
	}
	int BoxLength = 10;

	private void DrawBeginToOpen1(Graphics g) {
	  Point TopLeftCustom = new Point(TopLeftPoint.X - 10, TopLeftPoint.Y + 10);
	  Point BottomLeftCustom = new Point(TopLeftCustom.X, TopLeftCustom.Y + this.Height);
	  Point TopRightCustom = new Point(TopLeftPoint.X + this.Width - 10, TopLeftCustom.Y - 30);
	  Point BottomRightCustom = new Point(TopLeftPoint.X + this.Width - 10, TopRightCustom.Y + this.Height);
	  List < Point > listPoint = new List < Point > ();

	  listPoint.Add(TopLeftCustom);
	  listPoint.Add(BottomLeftCustom);
	  listPoint.Add(BottomRightCustom);
	  listPoint.Add(TopRightCustom);
	  g.FillPolygon(BackBrush, listPoint.ToArray());

	  Point DownSideBottomLeft = new Point(BottomLeftCustom.X + 10, BottomLeftCustom.Y + 10);
	  Point DownSideBottomRight = new Point(BottomRightCustom.X + 10, BottomRightCustom.Y + 10);
	  listPoint = new List < Point > ();
	  listPoint.Add(BottomLeftCustom);
	  listPoint.Add(DownSideBottomLeft);
	  listPoint.Add(DownSideBottomRight);
	  listPoint.Add(BottomRightCustom);
	  g.FillPolygon(BackBrush, listPoint.ToArray());

	  Point RightSideTopRight = new Point(TopRightCustom.X + 10, TopRightCustom.Y + 10);
	  Point RightSideBottomRight = new Point(BottomRightCustom.X + 10, BottomRightCustom.Y + 10);
	  listPoint = new List < Point > ();
	  listPoint.Add(TopRightCustom);
	  listPoint.Add(RightSideTopRight);
	  listPoint.Add(RightSideBottomRight);
	  listPoint.Add(BottomRightCustom);
	  g.FillPolygon(BackBrush, listPoint.ToArray());

	}

	private void DrawBeginToOpen2(Graphics g) {

	  Point TopLeftCustom = new Point(TopLeftPoint.X + BoxLength, TopLeftPoint.Y + BoxLength);
	  Point BottomLeftCustom = new Point(TopLeftCustom.X, TopLeftCustom.Y + this.Height);
	  Point TopRightCustom = new Point(TopLeftPoint.X + (this.Width / 2) - 10, TopLeftCustom.Y - (this.Height / 2));
	  Point BottomRightCustom = new Point(TopLeftPoint.X + (this.Width / 2) - 10, TopRightCustom.Y + this.Height);
	  List < Point > listPoint = new List < Point > ();

	  listPoint.Add(TopLeftCustom);
	  listPoint.Add(BottomLeftCustom);
	  listPoint.Add(BottomRightCustom);
	  listPoint.Add(TopRightCustom);
	  g.FillPolygon(BackBrush, listPoint.ToArray());

	  Point DownSideBottomLeft = new Point(BottomLeftCustom.X + BoxLength, BottomLeftCustom.Y);
	  Point DownSideBottomRight = new Point(BottomRightCustom.X + BoxLength, BottomRightCustom.Y);
	  listPoint = new List < Point > ();
	  listPoint.Add(BottomLeftCustom);
	  listPoint.Add(DownSideBottomLeft);
	  listPoint.Add(DownSideBottomRight);
	  listPoint.Add(BottomRightCustom);
	  g.FillPolygon(BackBrush, listPoint.ToArray());

	  Point RightSideTopRight = new Point(TopRightCustom.X + BoxLength, TopRightCustom.Y);
	  Point RightSideBottomRight = new Point(BottomRightCustom.X + BoxLength, BottomRightCustom.Y);
	  listPoint = new List < Point > ();
	  listPoint.Add(TopRightCustom);
	  listPoint.Add(RightSideTopRight);
	  listPoint.Add(RightSideBottomRight);
	  listPoint.Add(BottomRightCustom);
	  g.FillPolygon(BackBrush, listPoint.ToArray());
	}
	private void DrawBeginToOpen3(Graphics g) {
	  Point TopLeftCustom = new Point(TopLeftPoint.X + 20, TopLeftPoint.Y - 20);
	  Point BottomLeftCustom = new Point(TopLeftCustom.X, TopLeftCustom.Y + this.Height);
	  Point TopRightCustom = new Point(TopLeftPoint.X + this.Width - 20, TopLeftCustom.Y + this.BoxLength);
	  Point BottomRightCustom = new Point(TopRightCustom.X, TopRightCustom.Y + this.Height);
	  List < Point > listPoint = new List < Point > ();

	  listPoint.Add(TopLeftCustom);
	  listPoint.Add(BottomLeftCustom);
	  listPoint.Add(BottomRightCustom);
	  listPoint.Add(TopRightCustom);
	  g.FillPolygon(FaceBrushEmpty, listPoint.ToArray());

	  Point LeftSideTopLeft = new Point(TopLeftCustom.X - BoxLength, TopLeftCustom.Y + this.BoxLength);
	  Point LeftSideBottomLeft = new Point(LeftSideTopLeft.X, LeftSideTopLeft.Y + this.Height);
	  listPoint = new List < Point > ();
	  listPoint.Add(TopLeftCustom);
	  listPoint.Add(LeftSideTopLeft);
	  listPoint.Add(LeftSideBottomLeft);
	  listPoint.Add(BottomLeftCustom);
	  g.FillPolygon(BackBrush, listPoint.ToArray());

	  Point DownSideBottomRight = new Point(BottomRightCustom.X - BoxLength, BottomRightCustom.Y + BoxLength);
	  listPoint = new List < Point > ();
	  listPoint.Add(BottomLeftCustom);
	  listPoint.Add(LeftSideBottomLeft);
	  listPoint.Add(DownSideBottomRight);
	  listPoint.Add(BottomRightCustom);
	  g.FillPolygon(BackBrush, listPoint.ToArray());

	}
	private void DrawOpen(Graphics g) {
	  DrawClose(g);
	  Rectangle FillArea = new Rectangle(new Point(Left + 1, Top + 1), new Size(Width - 2, Height - 2));

	  g.FillRectangle(BackBrush, FillArea);
	  if (IsDog) {
	    g.DrawImage(DogImage, FillArea);
	  } else {
	    g.FillRectangle(FaceBrushEmpty, FillArea);
	  }

	}
```

For the ShuffleBox, 
the first parameter is Direction, there are 2 directions ClockWise, AntiClockWise,
the second is From
the third is to 

We have DictionaryPositionToBoxIndex to keep the indexposition
and boxs to keep the list of the box object.
The reason we need DictionaryPositionToBoxIndex  is
Supposing at the initial state we have boxes in this order.
box1, box2, box3

Then we shuffle between the first box and the second box, here is the result.
box3, box2, box1

If we would like to shuffle between the first box and the second box
we need to shuffle between box3 and box2
DictionaryPositionToBoxIndex can tell us
the firstbox is box3 not box1 anymore.

```C#
	private void DoShuffleBox(DirectionEnum Direction, int From, int To, int SpeedLevel) {
	  Box boxFrom = null;
	  Box boxTo = null;
	  int IndexFrom = DictionaryPositionToBoxIndex[From];
	  int IndexTo = DictionaryPositionToBoxIndex[To];

	  boxFrom = boxs[IndexFrom];
	  boxTo = boxs[IndexTo];

	  if (Direction == DirectionEnum.ClockWise) {
	    while (boxFrom.Top > TopPosition) {
	      boxFrom.Top -= SpeedLevel;
	      boxTo.Top += SpeedLevel;
	      pictureBox1.Refresh();

	    }
	    while (boxFrom.Left < listPositionBox[To].X) {
	      boxFrom.Left += SpeedLevel;
	      boxTo.Left -= SpeedLevel;
	      pictureBox1.Refresh();
	    }
	    while (boxFrom.Top < NormalTopPosition) {
	      boxFrom.Top += SpeedLevel;
	      boxTo.Top -= SpeedLevel;
	      pictureBox1.Refresh();

	    }
	  } else {
	    while (boxFrom.Top < BottopPosition) {
	      boxFrom.Top += SpeedLevel;
	      boxTo.Top -= SpeedLevel;
	      pictureBox1.Refresh();

	    }
	    while (boxFrom.Left < listPositionBox[To].X) {
	      boxFrom.Left += SpeedLevel;
	      boxTo.Left -= SpeedLevel;
	      pictureBox1.Refresh();
	    }
	    while (boxTo.Top < NormalTopPosition) {
	      boxTo.Top += SpeedLevel;
	      boxFrom.Top -= SpeedLevel;
	      pictureBox1.Refresh();

	    }
	  }
	  ShuffleBoxPosition(From, To);
	}
```

## Reference
This is a clone of Gecko Shuffle game
You can watch the the Original Gecko Shuffle here
[https://www.youtube.com/watch?v=u5SKVLbDNrE&ab_channel=258JD](https://www.youtube.com/watch?v=u5SKVLbDNrE&ab_channel=258JD)
[https://archive.org/details/win3_gecko](https://archive.org/details/win3_gecko)
[https://gamesdb.launchbox-app.com/games/images/105102](https://gamesdb.launchbox-app.com/games/images/105102)

puppy.png credit
[https://pngtree.com/so/cute](https://pngtree.com/so/cute)