If you're from the USA, and you've ever been to a Cracker Barrel, then you've probably played the board game where you have to jump pegs until you only have one left. It's similar to Chinese checkers, with just a pyramid, or a triangle.
I have a form that makes the buttons and adds them to the form, and I have a "TheBoard" class that has all of the rules for how jumping works on the form. In my form, I also have a button clicker method that needs to run all of this.
I seem to have hit a brick wall. I can't figure out the logic behind getting it to accept a second click, in order to move through the whole if statements in the board class. My parameter for the move method in the board class takes an int x, which is the button you click on as a parameter. I feel like I'm missing the second half of the move. How do I get my move method to register two button clicks (the starting location of the peg and the end location of the peg)?
Code for form:
public partial class Form1 : Form
{
private Button[] btn = new Button[15];
private TheBoard myboard = new TheBoard();
public Form1()
{
InitializeComponent();
int buttonsPerRow = 1;
int index = 0;
while (index < btn.Length)
{
int increment = this.Width / (buttonsPerRow + 1);
for (int j = 1; j <= buttonsPerRow; j++)
{
btn[index] = new Button
{
//other style elements of the button
Name = "btn" + index
}
btn[index].Click += new EventHandler(this.My_Click);
Controls.Add(btn[index]);
index++;
}
buttonsPerRow++;
}
}
private void My_Click(object sender, EventArgs e) {
myboard.getValues();
Button b = (Button)sender;
string bName = b.Name;
// Now pull off the btn
string num = bName.Substring(3, bName.Length - 3);
// Parsing the number to an int
int x = Int32.Parse(num);
myboard.move(x);
int[] color = myboard.getValues();
for (int i = 0; i < 15; i++)
{
color = myboard.getValues();
if (color[i] == TheBoard.hasPeg)
{
btn[i].BackColor = System.Drawing.Color.Yellow;
}
else
btn[i].BackColor = System.Drawing.Color.Black;
}//for
}
}
Code for TheBoard class:
class TheBoard
{
static public int hasPeg = 100;
static public int noPeg = 50;
private int[] board;
private int firstMove; //1st click
public TheBoard()
{
board = new int[15];
board[0] = noPeg;
for(int i = 1; i < 15; i++)
{
board[i] = hasPeg;
}
firstMove = -1; //giving last move a location, starting it at the beginning
}
public int move(int x)
{
if(firstMove == -1)
{
firstMove = x;
return 0;
}
// blank at 0
// if you click a blank your 1st move
if (firstMove == noPeg)
{
Console.WriteLine("You cant move if there isn't a peg.");
return 666;
}
// first---------------------------------------middle-----------------------end
if (firstMove == 1 && board[0] == hasPeg && board[3] == hasPeg && board[6] == noPeg)
{
RemovePeg(board[0], board[3], board[6]);
return 0;
}
if (firstMove == 1 && board[0] == hasPeg && board[2] == hasPeg && board[4] == noPeg)
{
RemovePeg(board[0], board[2], board[4]);
return 0;
}
//etc for remaining firstMove possibilities
firstMove = -1;
return 5;
}
private int RemovePeg(int first, int second, int goal) {
board[goal] = hasPeg;
board[first] = noPeg;
board[second] = noPeg;
return 0;
}
public int[] getValues()
{
return board;
}
}
I looked over the code and I think I understand your problem; you can select the starting peg but you don't have a way to select where it should go. With minimal edit to your code, I would store the first button click in a global variable and then the second button click knows that it is the second and initiates the board move with the two pieces of information (and resets the global variable).
Related
I'm trying to figure out how to set up my win condition (when the player lines up four chips as the same color horizontally, vertically, or diagonally). The win condition when met will display a win message, add 1 to a player win and player loss variable, add text to a list box, and clear the board.
I set up the board when the user presses the start button using the following code:
btnStartGame.Enabled = false;
btnStartGame.Visible = false;
btnExitGame.Enabled = true;
btnExitGame.Visible = true;
//This for loop creates the buttons used for the gameplay
for (int i = 0; i < gameButtons.Length; i++)
{
int index = i;
this.gameButtons[i] = new Button();
int x = 50 + (i % 7) * 50;
int y = 50 + (i / 7) * 50;
this.gameButtons[i].Location = new System.Drawing.Point(x, y);
this.gameButtons[i].Name = "btn" + (index + 1);
this.gameButtons[i].Size = new System.Drawing.Size(50, 50);
this.gameButtons[i].TabIndex = i;
this.gameButtons[i].UseVisualStyleBackColor = true;
this.gameButtons[i].Visible = true;
gameButtons[i].Click += (sender1, ex) => this.PlaceChip(sender1, index);
this.Controls.Add(gameButtons[i]);
From there main game play uses the following code for "dropping" chips into the columns:
private void PlaceChip(object sender, int index)
{
var pressedButton = (Button)sender;
if (pressedButton.BackColor == Color.BlanchedAlmond)
{
var newBackColor = black ? Color.Red : Color.Black;
var buttonToChangeIndex = index;
while (buttonToChangeIndex + 7 < gameButtons.Count() &&
gameButtons[buttonToChangeIndex + 7].BackColor == Color.BlanchedAlmond)
{
buttonToChangeIndex += 7;
}
gameButtons[buttonToChangeIndex].BackColor = newBackColor;
black = !black;
}
}
Currently my Win Condition code looks like the following, I just am not sure how I need to set this up correctly (assuming I am making a mistake somewhere) or how I call this and set up the arguments correctly when I call it.
private void WinCondition(int a, int b, int c, int d)
{
if (gameButtons[a].BackColor == gameButtons[b].BackColor && gameButtons[a].BackColor == gameButtons[c].BackColor && gameButtons[a].BackColor == gameButtons[d].BackColor)
{
gamesPlayed += 1;
do
{
if (gameButtons[a].BackColor == Color.Red)
{
MessageBox.Show("Player 1 Wins!");
player1wins += 1;
player2loss += 1;
lstScoreBoard.Items.Add("Player One");
//add to file
ResetBoard();
}
else
{
MessageBox.Show("Player 2 Wins!");
player2wins += 1;
player1loss += 1;
lstScoreBoard.Items.Add("Player Two");
ResetBoard();
}
if(gamesPlayed == 5)
{
MessageBox.Show("Maximum number of games have been played!\nWin board will now be reset!");
gamesPlayed = 0;
player1wins = 0;
player2wins = 0;
player1loss = 0;
player2loss = 0;
}
} while (gamesPlayed > 5);
}
}
If there is a better way to set up this win condition (In regards to the arguments etc) I am open for it! I'm at a loss for how to properly set it up!
If my description of what I need doesn't make sense feel free to ask and I will try to clarify!
I've been trying to figure out as to why my ascii people won't move independently as one figure, as they've been moving as one group.
My goal is to make a bunch of guys move in four random directions every frame. Every person is a class with it's own set of data, like the X and Y position of them. Here is a part of the code:
What the code does by the way, is it spawns 10 people in a rectangle of 10x2. This is when the problem arises.
public class Soldier
{
public int Health;
public string Symbol;
public int x, y;
public int ID;
public void SetHealth(int hp)
{
Health = hp;
}
public void SetSymbol(string sym)
{
Symbol = sym;
}
public void SetID(int id)
{
ID = id;
}
public void RandomStep()
{
Random rnd = new Random();
int randNum = rnd.Next(1, 5);
if (randNum == 1)
{
x += 1;
}
else if (randNum == 2)
{
x -= 1;
}
else if (randNum == 3)
{
y += 1;
}
else if (randNum == 4)
{
y -= 1;
}
}
}
else if (battleMode == true)
{
bool battleLoad = true;
int team1Size = 0;
int team1Max = 10;
int rowXstart = 40;
int rowYstart = 10;
int rowX = rowXstart;
int rowXmax = rowXstart + 5;
int rowY = rowYstart;
int rowYmax = 5;
int pickIDsol = 1;
int IDsoldier = 1;
int battleID = 1;
List<Soldier> soldierunlist = new List<Soldier>();
List<Soldier> soldierlist = new List<Soldier>();
for (; ; )
{
if (battleLoad == true)
{
for (; ; )
{
if (team1Size < team1Max)
{
soldierunlist.Add(new Soldier());
Soldier unpickSoldier = soldierunlist.First(x => x.ID == 0);
unpickSoldier.ID = IDsoldier;
IDsoldier += 1;
team1Size += 1;
Console.WriteLine("Loading1");
}
else
{
break;
}
}
foreach (Soldier soldier in soldierunlist) {
soldier.Symbol = "☻";
Console.WriteLine("Loading Graphics");
}
for (; ; )
{
if (soldierunlist.Count != 0)
{
if (rowX < rowXmax)
{
Soldier pickSoldier = soldierunlist.First(x => x.ID == pickIDsol);
pickSoldier.x = rowX;
pickSoldier.y = rowY;
rowX += 1;
pickIDsol += 1;
soldierunlist.Remove(pickSoldier);
soldierlist.Add(pickSoldier);
Console.WriteLine("Creating Position values for soldiers");
}
else if (rowX >= rowXmax)
{
rowX = rowXstart;
rowY += 1;
}
}
else
{
battleLoad = false;
battleMode = true;
break;
}
}
}
else
{
for (; ; ){
Console.Clear();
int RefreshRate = 100; // 1000 = 1 second
/// [ START ]
//foreach (Soldier soldier in soldierlist)
//{
// WriteAt(soldier.Symbol, soldier.x, soldier.y);
// soldier.RandomStep();
//}
for (; ; )
{
if (battleID != team1Max)
{
Soldier aSoldier = soldierlist.First(x => x.ID == battleID);
aSoldier.RandomStep();
battleID += 1;
}
else
{
break;
}
}
foreach (Soldier soldier in soldierlist)
{
WriteAt(soldier.Symbol, soldier.x, soldier.y);
}
battleID = 1;
/// [ END ]
System.Threading.Thread.Sleep(RefreshRate); // Waits for (number) of time
}
}
}
}
Random rnd = new Random();
creates a new random every time you want a random number. As all the calls will be about instant, you will end up with the same seed every time, so all move in the same direction.
From documentation (https://msdn.microsoft.com/en-us/library/system.random(v=vs.110).aspx):
However, because the clock has finite resolution, using the parameterless constructor to create different Random objects in close succession creates random number generators that produce identical sequences of random numbers. ... On most Windows systems, Random objects created within 15 milliseconds of one another are likely to have identical seed values. ... To avoid this problem, create a single Random object instead of multiple objects.
So, you need to create a single random object. Maybe at the loading of the game. Then use that single random to create directions to move in, which will be then different.
I have wrote and modified the source code of the classic snake game. As the level increases, more apples are being generated (e.g. level 1 will generate 1 apple, level 2 will generate 2 apple, and so on.) As i get to level 5, 5 apple were being generated, along with tremendous increases of my process memory, from 400MB to 2GB. That is where "ArgumentException" error pop up and the game crashed. Sorry for my bad coding as i am still learning.
The error showing to my draw() method where i called every 500ms for refreshing the board.
Error occurs at draw() method in Board.cs
public void draw(Position p, Image pic)
{
squares[p.getRowNo(), p.getColNo()].Image = pic;
}
refresh method in form1.cs
private void refresh(Object myObject, EventArgs myEventArgs)
{
mySnake.move(mode); //Move the snake based on mode
mainBoard.draw();
apples.draw(); //<----- draw apples
mySnake.draw();
//increment the duration by amount of time that has passed
duration += speed;
timerLBL.Text = Convert.ToString(duration / 1000); //Show time passed
//Check if snake is biting itself. If so, call GameOver.
if (mySnake.checkEatItself() == true)
{
GameOver();
}
else if (apples.checkIFSnakeHeadEatApple( mySnake.getHeadPosition()) == true)
{
score += apples.eatAppleAtPostion(mySnake.getHeadPosition());
scoreLBL.Text = Convert.ToString(score);
if (apples.noMoreApples() == true)
{
EatenAllApple();
clock.Stop();
level++;
gotoNextLevel(level);
MessageBox.Show("Press the start button to go to Level " + level, "Congrats");
}
else
{
//Length the snake and continue with the Game
mySnake.extendBody();
}
}
}
Overal Board.cs
class Board
{
int maxRow = 10, maxCol = 20; //Max 10 rows, and 20 columns in the board
int squareSize = 30; //Each square is 30px by 30px
PictureBox[,] squares;
public Board(Form mainForm)
{
squares = new PictureBox[maxRow, maxCol];
for (int row = 0; row < maxRow; row++)
{
for (int col = 0; col < maxCol; col++)
{
squares[row, col] = new PictureBox();
squares[row, col].Location = new Point(col * squareSize, row * squareSize);
squares[row, col].Height = squareSize;
squares[row, col].Width = squareSize;
squares[row, col].SizeMode = PictureBoxSizeMode.StretchImage;
squares[row, col].BackColor = Color.Black;
squares[row, col].BorderStyle = BorderStyle.FixedSingle;
mainForm.Controls["boardPanel"].Controls.Add(squares[row, col]);
}
}
mainForm.Controls["controlPanel"].Location = new Point(mainForm.Controls["boardPanel"].Location.X, mainForm.Controls["boardPanel"].Location.Y + mainForm.Controls["boardPanel"].Height + 20);
}
//setter
public void setMaxColNo(int x)
{
maxCol = x;
}
public void setMaxRowNo(int x)
{
maxRow = x;
}
//getter
public int getMaxColNo()
{
return maxCol-1; //Last Column No is 19, not 20
}
public int getMaxRowNo()
{
return maxRow-1; //Last Row No is 9, not 10
}
public int getMinColNo()
{
return 0; // 0 is the smallest Col number of the board
}
public int getMinRowNo()
{
return 0; // 0 is the smallest Row number of the board
}
public void draw()
{
for (int row = 0; row < maxRow; row++)
{
for (int col = 0; col < maxCol; col++)
{
squares[row, col].Image = null ;
}
}
}
public void draw(Position p, Image pic)
{
squares[p.getRowNo(), p.getColNo()].Image = pic;
}
}
Rewards.cs ( as requested by #AxelWass )
class Rewards
{
List<Position> appleList;
Board mainBoard;
public Rewards(int size, Board mainBoard)
{
this.mainBoard = mainBoard;
appleList = new List<Position>();
for (int i=0;i< size;i++)
{
int rowNo, colNo;
//Generate an apple at random position but not duplicated
do
{
//Generate a random number between 1 and MaxRowNo
rowNo = (new Random()).Next(1, mainBoard.getMaxRowNo()+1);
//Generate a random number between 1 and MaxColNo
colNo = (new Random()).Next(1, mainBoard.getMaxColNo()+1);
} while (isDuplicate(rowNo, colNo) == true);
appleList.Add(new Position(rowNo, colNo));
}
}
private Boolean isDuplicate(int row, int col)
{
Boolean result = false;
for (int i=0;i< appleList.Count;i++)
{
if (appleList[i].getRowNo() == row && appleList[i].getColNo() == col)
result = true;
}
return result;
}
public void draw()
{
for (int i = 0; i < appleList.Count; i++)
{
mainBoard.draw(appleList[i], Properties.Resources.apple);
}
}
public Boolean checkIFSnakeHeadEatApple(Position snakeHead)
{
Boolean result = false;
for (int i = 0; i < appleList.Count; i++)
{
if (snakeHead.getRowNo() == appleList[i].getRowNo() && snakeHead.getColNo() == appleList[i].getColNo())
result = true;
}
return result;
}
public Boolean checkIFSnakeEatApple(Position snakeHead)
{
Boolean result = false;
for (int i = 0; i < appleList.Count; i++)
{
if (snakeHead.getRowNo() == appleList[i].getRowNo() && snakeHead.getColNo() == appleList[i].getColNo())
result = true;
}
return result;
}
public int eatAppleAtPostion(Position p)
{
for (int i = 0; i < appleList.Count; i++)
{
if (p.getRowNo() == appleList[i].getRowNo() && p.getColNo() == appleList[i].getColNo())
appleList.RemoveAt(i);
//snakeEatApple();
}
return 50; //50 points per apple
}
public Boolean noMoreApples()
{
if (appleList.Count > 0)
return false;
else
return true;
}
/*public void snakeEatApple()
{
System.Media.SoundPlayer EatenApple = new System.Media.SoundPlayer(Properties.Resources.Eating_Apple);
EatenApple.Play();
}*/
}
mainBoard.draw(appleList[i], Properties.Resources.apple);
This is the problem statement. The resource designer in VS was not designed very well and violates Microsoft's own coding guidelines. What is not obvious at all is that the apple property creates a new Bitmap object every single time you use it. Since it is inside a loop, in a method that itself will be called very often, the code generates a lot of bitmap objects.
Bitmap is a disposable class. Not disposing it is in general pretty troublesome, it is a very small wrapper class that can use a lot of unmanaged memory. If the garbage collector doesn't run often enough so that the finalizer can run, the memory usage of your program can run up very quickly.
The workaround is to use the property only once. Store it in field of your class (outside of a method):
Bitmap apple = Properties.Resources.apple;
And fix the statement:
mainBoard.draw(appleList[i], apple);
And if you cross your T's and dot your I's then you dispose it when the form closes:
private void Form1_FormClosed(object sender, FormClosedEventArgs e) {
apple.Dispose();
}
Which is a good habit to get into, although it is probably unnecessary since your program probably ends when the user closes the window.
I am working on a Tic Tac Toe simulator for a class and have run into an issue.
I created a 2-dimensional array to simulate the board and populate it with either 0 or 1 in all the boxes.
The issue I am having is getting those numbers to apply to the labels I have created (a1, a2, a3, b1, b2, etcetera).
Is there a way that my nested for loops can have each element in the array apply to a new label? I can't seem to find anything in my book or online about this.
Here is my related code:
private void newBTN_Click(object sender, EventArgs e)
{
Random rand = new Random();
const int ROWS = 3;
const int COLS = 3;
int [,] board = new int[ROWS, COLS];
for (int row = 0; row < ROWS; row++)
{
for (int col = 0; col < COLS; col++)
{
board[row, col] = rand.Next(1);
}
}
}
What are the names of the labels? I assumed below that the labels are Label0_0, Label0_1, Label1_1 and so on... This way you can find them using the row and column values.
You want to find the Label control on your form dynamically, because you don't know the name in advance while coding.
If you know the name in advance you just say: label1.Text = "1";.
But in your case, you are trying to find a particular control in each iteration of the loop. So you need to have a name for the labels so you can find them using Form.Controls.Find(string, bool) like this:
var row = 4;
var col = 6;
var l = this.Controls.Find("Label" + row.ToString() + "_" + col.ToString(), false).FirstOrDefault() as Label;
if (l == null)
{
//problem... create label?
l = new Label() { Text = "X or O" }; //the position of this need to be set (the default is 0,0)
this.Controls.Add(l);
}
else
{
l.Text = "X or O";
}
Your board stores integers, which is an internal representation of your game state. You can create a UniformGrid that holds Label for your game GUI. The code below returns a grid based on your current board. You need to add this returned grid to your MainWindow (or whatever you use) to see it.
private UniformGrid fillLabels(int[,] board)
{
int numRow = board.GetLength(0);
int numCol = board.GetLength(1);
UniformGrid g = new UniformGrid() { Rows = numRow, Columns = numCol };
for (int i = 0; i < numRow; i++)
{
for (int j = 0; j < numCol; j++)
{
Label l = new Label();
l.Content = (board[i, j] == 0) ? "O" : "X";
Grid.SetRow(l, i);
Grid.SetColumn(l, j);
g.Children.Add(l);
}
}
return g;
}
First, do not re-create (and re-initialize) Random each time you need it: it makes generated sequences skewed badly:
private static Random s_Rand = new Random();
Try not implement algorithm in the button enent directly, it's a bad practice:
private void CreateField() { ... }
private void newBTN_Click(object sender, EventArgs e) {
CreateField();
}
putting all together:
private static Random s_Rand = new Random();
private void ApplyLabelText(String name, String text, Control parent = null) {
if (null == parent)
parent = this;
Label lb = parent as Label;
if ((lb != null) && (String.Equals(name, lb.Name))) {
lb.Text = text;
return;
}
foreach(Control ctrl in parent.Controls)
ApplyLabelText(name, text, ctrl);
}
private void CreateField() {
for (Char row = 'a'; row <= 'c'; ++row)
for (int col = 1; col <= 3; ++col)
ApplyLabelText(row.ToString() + col.ToString(), s_Rand.Next(1) == 0 ? "O" : "X");
}
private void newBTN_Click(object sender, EventArgs e) {
CreateField();
}
How about you skip the INTEGER board and go directly to a Label array?
You can then do the following to loop trough all of them:
Label[,] listOfLabels; // Do also initialize this.
foreach(Label current in listOfLabels)
{
current.Text = _rand.Next(2) == 0 ? "0" : "X";
}
Here is my code for a CheckXWinner function, where I need to reference my Form in order to draw win lines:
public void CheckXWinner(Button[] buttonArray, Form1 frm)
{
int arrLength = buttonArray.Length;
int root = (int)Math.Sqrt(Convert.ToDouble(arrLength));
bool winner = false;//variable to keep the computer from going when Xwins
for (int i = 0; i < root; i++)
{
//Sets the counter for the winners back to zero
int d2Count = 0;
int d1Count = 0;
int hCount = 0;
int vCount = 0;
for(int j = 0; j < root; j++)
{
//increments the appropriate counter if the button contains an X
//Horizonal win
if (buttonArray[(i*root) + j].Text == "X")
{
hCount++;
if (hCount == root)
{
for (int z = (root - 1); z >= 0; z--)
{
buttonArray[(i*root) + z].BackColor = Color.IndianRed;
}
Xwins();
winner = true; //sets winner to true so computer does not take turn
}
}//end of Horizonal win
//Left to right diagonal
if (buttonArray[j + (j*root)].Text == "X")
{
d1Count++;
if (d1Count == root)
{
for (int z = (root - 1); z >= 0; z--)
{
buttonArray[z + (z * root)].BackColor = Color.IndianRed;
}
Xwins();
winner = true;
}
}//end of LTR win
//Right to left diagonal
if (buttonArray[(j*(root - 1)) + (root - 1)].Text == "X")
{
d2Count++;
if (d2Count == root)
{
for (int z = (root - 1); z >= 0; z--)
{
buttonArray[(z*(root - 1)) + (root - 1)].BackColor = Color.IndianRed;
}
Xwins();
winner = true;
}
}//end of RTL win
//Vertical win
if (buttonArray[i + (root*j)].Text == "X")
{
vCount++;
if (vCount == root)
{
for (int z = (root - 1); z >= 0; z--)
{
buttonArray[i + (root*z)].BackColor = Color.IndianRed;
}
Xwins();
winner = true;
}
}//end of vert win
}//end of for j loop
}//end of for loop
CheckDraw();
if (winner == false)
{
ComputerGoes(buttonArray);
};
}//end of CheckXWinner
In another part of this class I have the handler for all button clicks associated with the form:
//Handle any button clicks
private void button_click(object sender, EventArgs e)
{
Button b = (Button)sender;
b.Text = "X";
b.Enabled = false;
CheckXWinner(buttonArray, Form1 frm);
}
I have errors for the Form1 part of that call, how do I fix this???
Where you are passing Form1 frm you should actually pass a reference to an instance on Form1. Based on your comment to the suggestion of passing this it seems that the button handler is declared inside another form (not in Form1). If that's the case you should obtain/keep a reference to a Form1 instance and pass that in:
CheckXWinner(buttonArray, a_ref_to_form);
However, looking at your implementation of CheckXWinner it doesn't look like you are referencing frm anywhere anyway!
Rewrite the declaration of CheckXWinner as
public void CheckXWinner(Button[] buttonArray)
And call it like this:
CheckXWinner(buttonArray);
frm isn't declared anywhere when you pass it as a parameter.
pass "this" instead of Form1 frm.