I'm currently working on an assignment to create a game of Connect 4 on a standard 7x6 grid using Visual Studio forms. I've searched on here already, but couldn't find a question that uses the same method of checking for a win that I am supposed to. I already have the architecture of the game completed; the board is fully functional, the pieces drop correctly, and already placed pieces can't be overwritten. However the catch of the assignment is that checking for a winner needs to implement an interface. This is the interface that's required:
interface Winner
{
//find winner in the specified direction starting from (c,r)
bool straightup(int c, int r);
bool straightdown(int c, int r);
bool left(int c, int r);
bool right(int c, int r);
bool diagleftup(int c, int r);
bool diagrightdown(int c, int r);
bool diagleftdown(int c, int r);
bool diagrightup(int c, int r);
//return true if there is winner
bool winner();
}
Here's the code for the board if it's needed:
bool isWinner, xTurn; //Keeps track if there is a winner and whose turn it is
int xWins, oWins; //Keeps track of each players' wins
//Keeps track of which row the next piece in the column will drop
int col1Drop, col2Drop, col3Drop, col4Drop, col5Drop, col6Drop, col7Drop;
Button[,] places; //Array of buttons on the board
public Form1()
{
InitializeComponent();
isWinner = false;
xTurn = true;
xWins = 0;
oWins = 0;
col1Drop = 0; col2Drop = 0; col3Drop = 0; col4Drop = 0; col5Drop = 0; col6Drop = 0; col7Drop = 0;
currentTurn.Text = "X";
winRecords.Text = "Record:" + Environment.NewLine + Environment.NewLine + "X Wins - 0"+ Environment.NewLine + "O Wins - 0";
//Initializes the array of buttons on the board, starting from left to right, bottom to top
places = new Button[,]{ { col1row1, col1row2, col1row3, col1row4, col1row5, col1row6 }, { col2row1, col2row2, col2row3, col2row4, col2row5, col2row6 }, { col3row1, col3row2, col3row3, col3row4, col3row5, col3row6 }, { col4row1, col4row2, col4row3, col4row4, col4row5, col4row6 }, { col5row1, col5row2, col5row3, col5row4, col5row5, col5row6 }, { col6row1, col6row2, col6row3, col6row4, col6row5, col6row6 }, { col7row1, col7row2, col7row3, col7row4, col7row5, col7row6 } };
}
private void col1row6_Click(object sender, EventArgs e) //Runs when top button on column is clicked, all other buttons in column are disabled
{
if(xTurn) //Plays if its X's turn
{
if (col1Drop < 6) //Places piece as long as there's still a spot available
{
places[0, col1Drop].BackgroundImage = GUI_Connect4.Properties.Resources.X;
places[0, col1Drop].BackgroundImageLayout = ImageLayout.Stretch; //Fills the next available row in the column
xTurn = false; //Changes it to next player's turn
currentTurn.Text = "O";
col1Drop++; //Moves to next avaiable column
}
}
else //Plays if it's O's turn
{
if (col1Drop < 6)
{
places[0, col1Drop].BackgroundImage = GUI_Connect4.Properties.Resources.O;
places[0, col1Drop].BackgroundImageLayout = ImageLayout.Stretch;
xTurn = true;
currentTurn.Text = "X";
col1Drop++;
}
}
} //This code is the same for each column, with values changed accordingly
How would I go about checking for a win, and where should the interface be implemented? I'm assuming it should just be implemented in a separate checkWinner method that runs whenever a piece is played.
Related
I'm developing a theatre reservation software. I'm using Windows Forms, the seats is represented by a 2-dimensioned array. And I draw the buttons as following:
public void DrawSeats()
{
// pnl_seats is a Panel
pnl_seats.Controls.Clear();
// Here I store all Buttons instance, to later add all buttons in one call (AddRange) to the Panel
var btns = new List<Control>();
// Suspend layout to avoid undesired Redraw/Refresh
this.SuspendLayout();
for (int y = 0; y < _seatZone.VerticalSize; y++)
{
for (int x = 0; x < _seatZone.HorizontalSize; x++)
{
// Check if this seat exists
if (IsException(x, y))
continue;
// Construct the button with desired properties. SeatSize is a common value for every button
var btn = new Button
{
Width = SeatSize,
Height = SeatSize,
Left = (x * SeatSize),
Top = (y * SeatSize),
Text = y + "" + x,
Tag = y + ";" + x, // When the button clicks, the purpose of this is to remember which seat this button is.
Font = new Font(new FontFamily("Microsoft Sans Serif"), 6.5f)
};
// Check if it is already reserved
if (ExistsReservation(x, y))
btn.Enabled = false;
else
btn.Click += btn_seat_Click; // Add click event
btns.Add(btn);
}
}
// As said before, add all buttons in one call
pnl_seats.Controls.AddRange(btns.ToArray());
// Resume the layout
this.ResumeLayout();
}
But already with a seat zone of 20 by 20 (400 buttons), it spent almost 1 minute to draw it, and in debug I checked that the lack of performance, is the instantiation of the buttons.
There is a way to make it faster? Perhaps disable all events during the instatiation or another lightweight Control that has the Click event too?
UPDATE:
lbl was a test, the correct is btn, sorry.
UPDATE 2:
Here is the IsException and ExistsReservations methods:
private bool IsException(int x, int y)
{
for (var k = 0; k < _seatsExceptions.GetLength(0); k++)
if (_seatsExceptions[k, 0] == x && _seatsExceptions[k, 1] == y)
return true;
return false;
}
private bool ExistsReservation(int x, int y)
{
for (var k = 0; k < _seatsReservations.GetLength(0); k++)
if (_seatsReservations[k, 0] == x && _seatsReservations[k, 1] == y)
return true;
return false;
}
Suppose that you change your arrays for reservations and exclusions to
public List<string> _seatsExceptions = new List<string>();
public List<string> _seatsReservations = new List<string>();
you add your exclusions and reservations in the list with something like
_seatsExceptions.Add("1;10");
_seatsExceptions.Add("4;19");
_seatsReservations.Add("2;5");
_seatsReservations.Add("5;5");
your checks for exclusions and reservations could be changed to
bool IsException(int x, int y)
{
string key = x.ToString() + ";" + y.ToString();
return _seatsExceptions.Contains(key);
}
bool ExistsReservation(int x, int y)
{
string key = x.ToString() + ";" + y.ToString();
return _seatsReservations.Contains(key);
}
of course I don't know if you are able to make this change or not in your program. However consider to change the search on your array sooner or later.
EDIT I have made some tests, and while a virtual grid of 20x20 buttons works acceptably well (31 millisecs 0.775ms on average), a bigger one slows down noticeably. At 200x50 the timing jumps to 10 seconds (1,0675 on average). So perhaps a different approach is needed. A bound DataGridView could be a simpler solution and will be relatively easy to handle.
I also won't use such a myriad of controls to implement such a thing. Instead you should maybe create your own UserControl, which will paint all the seats as images and reacts on a click event.
To make it a little easier for you i created such a simple UserControl, that will draw all the seats and reacts on a mouse click for changing of the state. Here it is:
public enum SeatState
{
Empty,
Selected,
Full
}
public partial class Seats : UserControl
{
private int _Columns;
private int _Rows;
private List<List<SeatState>> _SeatStates;
public Seats()
{
InitializeComponent();
this.DoubleBuffered = true;
_SeatStates = new List<List<SeatState>>();
_Rows = 40;
_Columns = 40;
ReDimSeatStates();
MouseUp += OnMouseUp;
Paint += OnPaint;
Resize += OnResize;
}
public int Columns
{
get { return _Columns; }
set
{
_Columns = Math.Min(1, value);
ReDimSeatStates();
}
}
public int Rows
{
get { return _Rows; }
set
{
_Rows = Math.Min(1, value);
ReDimSeatStates();
}
}
private Image GetPictureForSeat(int row, int column)
{
var seatState = _SeatStates[row][column];
switch (seatState)
{
case SeatState.Empty:
return Properties.Resources.emptySeat;
case SeatState.Selected:
return Properties.Resources.choosenSeat;
default:
case SeatState.Full:
return Properties.Resources.fullSeat;
}
}
private void OnMouseUp(object sender, MouseEventArgs e)
{
var heightPerSeat = Height / (float)Rows;
var widthPerSeat = Width / (float)Columns;
var row = (int)(e.X / widthPerSeat);
var column = (int)(e.Y / heightPerSeat);
var seatState = _SeatStates[row][column];
switch (seatState)
{
case SeatState.Empty:
_SeatStates[row][column] = SeatState.Selected;
break;
case SeatState.Selected:
_SeatStates[row][column] = SeatState.Empty;
break;
}
Invalidate();
}
private void OnPaint(object sender, PaintEventArgs e)
{
var heightPerSeat = Height / (float)Rows;
var widthPerSeat = Width / (float)Columns;
e.Graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
for (int row = 0; row < Rows; row++)
{
for (int column = 0; column < Columns; column++)
{
var seatImage = GetPictureForSeat(row, column);
e.Graphics.DrawImage(seatImage, row * widthPerSeat, column * heightPerSeat, widthPerSeat, heightPerSeat);
}
}
}
private void OnResize(object sender, System.EventArgs e)
{
Invalidate();
}
private void ReDimSeatStates()
{
while (_SeatStates.Count < Rows)
_SeatStates.Add(new List<SeatState>());
if (_SeatStates.First().Count < Columns)
foreach (var columnList in _SeatStates)
while (columnList.Count < Columns)
columnList.Add(SeatState.Empty);
while (_SeatStates.Count > Rows)
_SeatStates.RemoveAt(_SeatStates.Count - 1);
if (_SeatStates.First().Count > Columns)
foreach (var columnList in _SeatStates)
while (columnList.Count > Columns)
columnList.RemoveAt(columnList.Count - 1);
}
}
This will currently draw forty rows and columns (so there are 800 seats) and you can click on each seat to change its state.
Here are the images i used:
EmtpySeat:
ChoosenSeat:
FullSeat:
If you anchor this control and resize it or you click on a seat to change its state there can be some minor lacking for the repainting if you further increase the number of rows or columns, but that is still somewhere far below one second. If this still hurts you, you have to improve the paint method and maybe check the ClipRectangle property of the paint event and only paint the really needed parts, but that's another story.
Rather than using actual button controls, just draw the image of the seats then when the user clicks on a seat translate the mouse X,Y coordinates to determine which seat was clicked. This will be more efficient. Of course, the drawback is that you have to write the method to translate x,y coordinates to a seat, but that really isn't that difficult.
EDIT; it has been pointed out to me this will not work in Windows Forms!
Well, you are Sequentially working through it.
if one iteration costs 1 sec, the full process will take 400*1 in time.
Perhaps you should try and make a collection of your objects, and process it 'parallel'.
try the .Net framework (4 and above) 'parallel foreach' method:
http://msdn.microsoft.com/en-s/library/system.threading.tasks.parallel.foreach(v=vs.110).aspx
edit: so, if you have a list buttonnames, you can say
buttonNames.ForEach(x=>CreateButton(x));
while your CreateButton() method is as following:
private Button CreateButton(string nameOfButton) { Button b = new
Button(); b.Text = nameOfButton; //do whatever you want...
return b; }
I'm fairly new to OOP and am not sure how I would go about implementing something in my program. My program is pretty much similar to whack a mole and has an array of picture boxes with an image in and an image of a monster moves randomly between the picture boxes with a time interval applied or will move to a new random picture box whenever the user clicks on the monster in time. I have created an monster and a player sub class to try and add some OOP concepts to the program but am not sure how to implement what I want. Basically I have a label for score on my main form and a score variable in my animal class with a value. I want to be able to add the value of score from the label on my form when the user clicks on the picture box with the mole in and take away the value of score from the label when they don't click on it in time.
Here is my code:
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
PictureBox[] boxes;
int initialscore = 0;
int time = 0;
int randPos;
public Form1()
{
InitializeComponent();
}
private void boxes_MouseClick(object sender, MouseEventArgs e)
{
PictureBox pb2 = new PictureBox() { Image = Image.FromFile("sword2.png") };
this.Cursor = new Cursor(((Bitmap)pb2.Image).GetHicon());
for (int x = 0; x < 27; x++)
{
if (sender.Equals(boxes[x]))
{
Image grass = Image.FromFile("swamp.png");
PictureBox temp = (PictureBox)sender;
temp.Image = grass;
}
if (sender.Equals(boxes[x]))
{
PictureBox pb = (PictureBox)sender;
if (pb.Tag == "skeleton.png")
initialscore++;
}
}
label1.Text = " Score: " +initialscore.ToString();
}
public void timer1_Tick(object sender, EventArgs e)
{
boxes[randPos].Image = Image.FromFile("swamp.png");
boxes[randPos].Tag = "swamp.png";
Random r = new Random();
randPos=r.Next(0, 27);
boxes[randPos].Image = Image.FromFile("skeleton.png");
boxes[randPos].Tag = "skeleton.png";
}
private void Form1_Load(object sender, EventArgs e)
{
boxes = new PictureBox[27];
int top = 100;
int left = 100;
for (int x = 0; x < 27; x++)
{
boxes[x] = new PictureBox();
boxes[x].Image = Image.FromFile("swamp.png");
boxes[x].Height = 100;
boxes[x].Width = 100;
if (x % 9 == 0)
{
top += 120;
left = 120;
}
else
left += 120;
boxes[x].Top = top;
boxes[x].Left = (50 + left);
Controls.Add(boxes[x]);
this.boxes[x].MouseClick += new
System.Windows.Forms.MouseEventHandler(this.boxes_MouseClick);
label1.Text = " Score: " + initialscore.ToString();
label2.Text = " Time: " + time.ToString();
}
}
}
namespace WindowsFormsApplication1
{
class Monster
{
protected int score;
public Monster()
{
score = 10;
}
}
}
namespace WindowsFormsApplication1
{
class Player:Monster
{
}
}
Nothing has been added in the player class yet.
What do I need to add or change to be able to get the initial score to change by the value of the score in the monster class when clicking on the moving image?
To unify the updating/incrementing and visualization of the score you should extract that to a method:
public void incrementScore(int increment)
{
initialscore += increment;
label1.Text = " Score: " + initialscore.ToString();
}
in the Form1_Load you call this like:
incrementScore(0);
for the click on the monster you have different possibilities:
if all the monsters have the same points you can make it a static variable in the Monster class.
protected static int Score = 10;
which allows you to use it in the boxes_MouseClick event handler:
incrementScore(Monster.Score);
in case all monsters have another value you have to hold the score variable as an instance variable, identify somehow the instance of the monster class you clicked on and increment with this value
public void ItemGot()
{
number = number + 1; //Increment by 1
quantity.Text = ("x" + number); //Overwrite Text
quantity.Refresh(); //Updates the text
}
Hello, I have this code above. When this method runs, the text of a label I set up earlier should change the text to the one i set below. However, its not doing it. Furthermore, by setting breakpoints in Visual Studio, I have determined that:
The method is being called,
Number is being incremented properly.
There should be NO reason why this not working, because the program is recognizing that number is increasing by one. My friend said a similar question here. Still no help, and now that code is outdated. Please help!
EDIT: how I added the quantity label
First, I initialized it in the constructor: public Label quantity;
Then I did this: quantity = new Label();
Lastly, in another method, I gave the quantity the following properties:
quantity.Size = new Size(24, 24);
quantity.Text = ("x" + number);
quantity.Left = 48;
Controls.Add(quantity);
number is also in the constructor and is set to 0.
EDIT 2 : I'll Post my whole method
public InventoryScreen()
{
btnItems = new Button();
quantity = new Label();
//call the methods for spawning the buttons
ButtonGenItems(cNumber, btnItems, quantity);
InitializeComponent();
}
public void InventoryScreen_Load(object sender, EventArgs e)
{
}
#region ButtonGenItems Method
public void ButtonGenItems(int cNumber, Button btnItems,Label quantity)
{
int xPos = 126;
int yPos = 25;
for (int n = 0; n < 1; n++)
{
btnItems.Tag = n;
btnItems.Size = new Size(48, 52); //Button size X and Y
btnItems.BackColor = Color.CornflowerBlue;
quantity.Size = new Size(24, 24);
quantity.Text = ("x" + number);
if (yPos > 60) // Five Buttons in one column
{
yPos = 25; //spawn position Y
xPos = xPos + btnItems.Width + 10; //spacing X
}
btnItems.Left = xPos; //Start Button spawn at the Left side
btnItems.Top = yPos; //Start spawn at the top side
quantity.Left = 48;
quantity.Top = 60;
yPos = yPos + btnItems.Height + 10;
btnItems.Text = "Use";
Controls.Add(btnItems); //place Buttons
Controls.Add(quantity);
// the Event of click Button
//btnItems.Click += new System.EventHandler(ItemUse); //to be implimented
}
}
#endregion
public void ItemGot()
{
//*Interestingly, the program recognizes that 'number' is increasing by 1, but label won't update the text
//Furthermore, pressing the actual button will trigger the text update, but simulating a buttonclick WONT DO ANYTHING
Console.WriteLine("Text should now increment by 1"); //Debugging to test method
number = number + 1; //Increment by 1
quantity.Text = ("x" + number); //Overwrite Text
}
}
2.5 This is how the method is being called. This method is located in another class
public void Update(Vector2 pos)
{
this.position = pos; //get char position
Inv = new InventoryScreen(); //create instance of object
charRange = new Rectangle((int)position.X, (int)position.Y, 64, 57); //create rectangle
//Intersection Code, If the character intersects with the item while the item is showing, run below
if (alive && charRange.Intersects(itemRect))
{
alive = false; //stop showing the item
Inv.ItemGot(); //Call the ItemGot class, which adds the item to the inventory screen
}
}
As I understand you have already open/showed form(instance of class InventoryScreen) with your label when you calling Update method, But...
Inside of method Update you creating a new instance of InventoryScreen, and calling function ItemGot with this new instance of form.
I think you need to pass reference of your current instance of InventoryScreen in method Update, then use that reference for calling ItemGot method
public void Update(Vector2 pos, InventoryScreen invscreen)
{
this.position = pos;
charRange = new Rectangle((int)position.X, (int)position.Y, 64, 57);
if (alive && charRange.Intersects(itemRect))
{
alive = false;
invscreen.ItemGot();
}
}
Source not available keeps on popping up
I have been searching google for quite some time now and most of the solutions I saw were changing Visual Studio's Settings, tried them but still it doesn't solve the issue
I have the following code
[DllImport("BS2dll.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.I1)]
public static extern bool FiRE(int ptr, int l);
in another form
public void createButton2()
{
int x = 0, y = 0;
butt2 = new Button[100];
for (int i = 0; i < 100; i++)
{
butt2[i] = new Button();
int names = i;
butt2[i].Name = "2" + names.ToString();
butt2[i].Location = new Point(520 + (x * 31), 70 + (y * 21));
butt2[i].Visible = true;
butt2[i].Size = new Size(32, 22);
butt2[i].BackColor = Color.CornflowerBlue;
this.Controls.Add(butt2[i]);
butt2[i].Click += new EventHandler(butt2_2_Click);
x++;
if (x == 10)
{
x = 0; y++;
}
}
}
event handler
private void butt2_2_Click(object sender, EventArgs e)
{
bool hit;
int loc;
Button pushedBtn = sender as Button;
if (pushedBtn != null)
{
pushedBtn.BackColor = Color.Blue;
pushedBtn.Enabled = false;
loc = int.Parse(pushedBtn.Name);
if (loc > 29)
loc -= 200;
else
loc -= 20;
hit = UnsafeMethodCall.FiRE(objPtr, loc);
if (hit == true)
{
// MessageBox.Show("HIT");
pushedBtn.BackColor = Color.Red;
}
else
{
//MessageBox.Show("MISS");
pushedBtn.BackColor = Color.Black;
}
}
}//error appears around here
even if I comment hit = UnsafeMethodCall.FiRE(objPtr, loc); it still persists
my dll if it helps
void __stdcall FiRE(int *ptr, int l)
{
Battleship *dll = (Battleship *) ptr;
dll->FIRE(l);
}
bool Battleship::FIRE( int l) //check if hit or miss
{
for(int i= 0; i<17; i++)
{
if (board[i]==l)
{
hit = true;
break;
}
else
hit = false;
break;
}
return hit;
}
My Settings
Settings
so I think my problem is at the event handling part, since even if I remove the call to the dll, error still persists
as you may have noticed, it is a battleship game
when I hit a ship for the first time, it returns true, next attempts would just return false
You should check and make sure all of your source files are being looked at, especially for external libraries that you imported. Go to the solution and right the properties. In the window the pops up [below] go to the Debug Source Files.
I'm creating a small game just like the game Reversi/Othello I have managed to created a 2x3 board with buttons.
The buttons change colour ones you click on them but I'm having trouble to detect if there is a white colour in between 2 black colours and if so change that white colour into black.. I hope this make sense. the buttons are in a 2D array. Any suggestions that could help me do this would be much appreciated.
The image:
Here is my code:
![namespace reversitest
{
public partial class Form1 : Form
{
private Button\[,\] squares;
public Form1()
{
InitializeComponent();
squares = new Button\[3, 2\];
squares = new Button\[,\] {{button1, button2, button3},
{button4, button5, button6,}};
}
private void Form1_Load(object sender, EventArgs e)
{
foreach (Button sqrr in squares)
{
sqrr.Click += new System.EventHandler(this.DrawCharacter);
}
}
int _turn = 0;
private void DrawCharacter(object sender, EventArgs e)
{
Button sqrr = (Button)sender;
int col = 0;
if (sqrr.BackColor.Equals(Color.Black) || sqrr.BackColor.Equals(Color.White))
{
MessageBox.Show("Move Not Allowed!");
}
else
{
for ( int i = 0; i < squares.GetLongLength(1); ++i)
{
// check othere squares and change color
if (i < 2)
{
for (int f = 0; f < 3; ++f)
{
var ss = squares\[i, f\];
if (ss.BackColor.Equals(Color.Black))
{
MessageBox.Show("we have a black");
//ss = squares\[i, f+1\];
ss.BackColor = Color.Black;
}
else
{
MessageBox.Show("no black");
}
}
}
if (_turn == 0)
{
_turn = 1;
sqrr.BackColor = Color.Black;
}
else
{
_turn = 0;
sqrr.BackColor = Color.White;
}
}
}
}
}
}
First name your buttons with the array index. It will help you to find the button.
For example according to you picture button1 name would be btn_1_1.
Then inside your button click event first get the button name and then identify the button positioned.
Button b = sender as Button;
string[] btnData = b.Name.Split('_');
int x = int.Parse(btnData[1]);
int y = int.Parse(btnData[2]);
//check for possible combinations
int top = y - 2;
int botton = y + 2;
int left = x - 2;
int right = x + 2;
if (top >= 0 && squares[top, y].Background == Color.Black)
{
squares[top+1, y].Background = Color.Black;
}
...
...
Continue like that. If you need more detail please free to ask.
Final Answer
//check for possible combinations
int top = x - 2;
int botton = x + 2;
int left = y - 2;
int right = y + 2;
if (top >= 0 && squares[top, y].BackColor == Color.Black)
{
squares[top + 1, y].BackColor = Color.Black;
}
else if (left >= 0 && squares[x, left].BackColor == Color.Black)
{
squares[x, left + 1].BackColor = Color.Black;
}
else if (left >= 0 && squares[x, left].BackColor == Color.Black)
{
squares[x, left + 1].BackColor = Color.Black;
}
Will be extended for a 8x8 board later on
Do you need it to be elegant? A kind of brute force method: You could check for pieces in the 8 different directions it is possible for them to be aligned. So for example, you start with a black piece. Check the next piece over in one direction. If it's white, keep going and take a note of the position that was white so you can change it to black later. When you finally hit a black piece, change all the stored positions to black and move on to the next direction and repeat the process until you've done all 8 directions.