Tic Tac Toe recursive algorithm - c#

I can see this question (or a similar one) has been asked a few times and I've searched google a lot so that I can try to understand it however I am most definitely stuck.
My task is to use a recursive function that uses a "goodness" variable to determine which is the best move a computer can make, I even have a document that is meant to help with this but for the life of me, I just don't understand it.
If anyone could take some time to help me or break down what I actually need to do i'd be much appreciating, I'll link the code I have so far below but this is an assignment so guidance is preferable to direct answers. I've looked at the MinMax solution and that definitely seems above my grasp, I am very new to programming (especially in C# with only a few months experience) so go easy!
Here is the proposed solution I'm meant to be following:
http://erwnerve.tripod.com/prog/recursion/tictctoe.htm
public partial class Form1 : Form
{
public static string[,] Board = new string[3, 3] { { "1", "2", "3" }, { "4", "5", "6" }, { "7", "8", "9" } };
public bool Winner = false;
public string WinState;
private void Reset()
{
WinState = "";
Winner = false;
Board[0, 0] = "1";
Board[0, 1] = "2";
Board[0, 2] = "3";
Board[1, 0] = "4";
Board[1, 1] = "5";
Board[1, 2] = "6";
Board[2, 0] = "7";
Board[2, 1] = "8";
Board[2, 2] = "9";
btn1.Text = "";
btn2.Text = "";
btn3.Text = "";
btn4.Text = "";
btn5.Text = "";
btn6.Text = "";
btn7.Text = "";
btn8.Text = "";
btn9.Text = "";
}
private void checkWinner()
{
// Top Row
if (Board[0, 0].Equals(Board[0, 1]) && Board[0, 1].Equals(Board[0, 2]))
{
Winner = true;
WinState = Board[0, 0];
}
// Middle Row
if (Board[1, 0].Equals(Board[1, 1]) && Board[1, 1].Equals(Board[1, 2]))
{
Winner = true;
WinState = Board[1, 0];
}
// Bottom Row
if (Board[2, 0].Equals(Board[2, 1]) && Board[2, 1].Equals(Board[2, 2]))
{
Winner = true;
WinState = Board[2, 0];
}
// Left column
if (Board[0, 0].Equals(Board[1, 0]) && Board[1, 0].Equals(Board[2, 0]))
{
Winner = true;
WinState = Board[0, 0];
}
// Middle column
if (Board[0, 1].Equals(Board[1, 1]) && Board[1, 1].Equals(Board[2, 1]))
{
Winner = true;
WinState = Board[0, 1];
}
// Right column
if (Board[0, 2].Equals(Board[1, 2]) && Board[1, 2].Equals(Board[2, 2]))
{
Winner = true;
WinState = Board[0, 2];
}
// Diagonal 1
if (Board[0, 0].Equals(Board[1, 1]) && Board[1, 1].Equals(Board[2, 2]))
{
Winner = true;
WinState = Board[0, 0];
}
// Diagonal 2
if (Board[2, 0].Equals(Board[1, 1]) && Board[1, 1].Equals(Board[0, 2]))
{
Winner = true;
WinState = Board[2, 0];
}
if (Winner == true)
{
if (WinState == "X")
{
MessageBox.Show("Congratulations you win!");
Reset();
}
else if (WinState == "O")
{
MessageBox.Show("Sorry you lose!");
Reset();
}
}
}
private void btn1_Click(object sender, EventArgs e)
{
btn1.Text = "X";
Board[0, 0] = "X";
checkWinner();
}
private void btn2_Click(object sender, EventArgs e)
{
btn2.Text = "X";
Board[0, 1] = "X";
checkWinner();
}
private void btn3_Click(object sender, EventArgs e)
{
btn3.Text = "X";
Board[0, 2] = "X";
checkWinner();
}
private void btn4_Click(object sender, EventArgs e)
{
btn4.Text = "X";
Board[1, 0] = "X";
checkWinner();
}
private void btn5_Click(object sender, EventArgs e)
{
btn5.Text = "X";
Board[1, 1] = "X";
checkWinner();
}
private void btn6_Click(object sender, EventArgs e)
{
btn6.Text = "X";
Board[1, 2] = "X";
checkWinner();
}
private void btn7_Click(object sender, EventArgs e)
{
btn7.Text = "X";
Board[2, 0] = "X";
checkWinner();
}
private void btn8_Click(object sender, EventArgs e)
{
btn8.Text = "X";
Board[2, 1] = "X";
checkWinner();
}
private void btn9_Click(object sender, EventArgs e)
{
btn9.Text = "X";
Board[2, 2] = "X";
checkWinner();
}
}

Don't feel too bad about not understanding recursion as a result of reading that document. It doesn't do a very good job of explaining recursion. (It's a tough concept - I probably won't do that well either). Ultimately, what you have to do is try to make your program do what you would do. I'm going to try to explain it from that point of view.
Recursion is useful, because it lets us code (once) a step in a solution, and then repeat that step using as input the results that were just calculated. Try to look at your problem from your point of view, not some arbitrary goodness algorithm. You're probably trying too hard to understand the algorithm from the paper.
Try thinking like this: At the start of the game player 1 makes a play. Your program has to choose a move for Player 2. But player 2 has to think about the rest of the game (FOR EACH POSSIBLE MOVE).
Player 2 can choose from 8 possible moves.
Player 1 can choose from 7
Player 2 can choose from 6
Player 1 can choose from 5
Player 2 can choose from 4
Player 1 can choose from 3
Player 2 can choose from 2
Player 1 takes the last square.
You can re-word this into:
current player is 2, give a weight to all possible remaining choices for the current player.
current player is 1, give a weight to all possible remaining choices for the current player.
current player is 2, give a weight to all possible remaining choices for the current player.
current player is 1, give a weight to all possible remaining choices for the current player.
current player is 2, give a weight to all possible remaining choices for the current player.
current player is 1, give a weight to all possible remaining choices for the current player.
current player is 2, give a weight to all possible remaining choices for the current player.
current player is 1, give a weight to all possible remaining choices for the current player.
You can reword this into:
given current player,
switch player and give a weight to all possible choices for the current player.
Repeat until no more choices are possible
You can reword this into:
given current player,
switch player and CheckGoodness()
Repeat until no more choices are possible
So, back to your write-up. The author uses players of 1 & -1. Why? Because as you pass the moves deeper and deeper, you must swap players, and it's very easy to switch players as you go down levels like this (I'm only talking about player here:
public int CheckGoodness(bool playerID)
{
playerID = -playerID;
if (!endConditionMet)
{
goodness = CheckGoodness(playerID);
}
return goodness;
}
Along with the player, you have to pass something that represents the state of all possible moves remaining. The problem is that if you pass something that passes as a reference, any change you make will be reflected in your original data. Make sure that isn't happening. That is probably why #CodeInChaos suggested you clone.
Note that in recursion you have to make sure you ALWAYS have a way to end the call sequence. You must modify whatever your end condition is relying on. In this case, your number of possible moves is dropping. Otherwise you call forever and run out of memory.
EDIT: Explanation of board class added.
Think about it from the big picture. A class is a representation of a real world thing (e.g. an object). A thing has attributes that describe it. These are the class's data. A thing also does actions. These are the methods. Another definition of a class that I've heard is a class is data and operations on that data.
Think about what objects the game has. 2 players and a board. Not much else.
A player can move, and has a unique identifier (in this case either 'X' or 'O'), and can be either human or AI. I can't think of anything else (that matters) at the moment, but there are usually more things that could be there but don't really affect the program (like eye color). This is also where you could use inheritance. You have a player class with an abstract move method. A human class that inherits from player with an override move method that accepts input from the UI, a computer/AI class that inherits from player and overrides the move method by calculating the move with the goodness rating.
The board has data:
a 3 by 3 grid of possible play locations (This COULD be a collection of location objects too, by the way)
might need representation for players 1 & 2
The actions of the board COULD be:
accept a player's (Human or AI) move, records it if valid, determines win & returns an indicator of good move, bad move, game over or win
Could have a method to return winner of current game
Probably need a reset method
Could have a move history
You could have a static GoodNess class (might need a better name)
with no data but a single method (or this could be another method on the board class:
accept a board, calculate & return goodness array, or simply return best move
The AI could call the Goodness GetBestMove method before making a move.
The recursion would be isolated to that GetBestMove method.
Note that none of these are set in stone. Classes are defined by what you think should be in it. It's all based on how you percieve would be the best way to solve the problem. If you still have trouble, update your question with the code you've attempted to make work. It really helps to draw out diagrams as you start to lay out your code.
Good luck, hope this helps, and I'll try to do a better job of monitoring StackOverflow notifications.

Related

add Multiple controls for a Game like Snake

To learn and of course for fun I'm building a snake imitation, the only difference should be that instead of an endless chain, a lot of enemies or objects appear on the map and the game should be made more and more difficult.
The code snippet is supposed to show how I create my controls, but I have the problem that only the last created control is actually captured by my "IntersectsWith", all those created before that, as well as the very first one, are then no longer any opponents for me and my player just goes through them.
What is the best way to do this so that all created objects can be found under one definition so that I don't have to write hundreds of IntersectsWith If statements.
I would like to learn how to dynamically create controls and also dynamically have direct access to them, in which my player can interact with them through collision.
I really thought I could solve this with a random character generator and different control names can solve this, but alas - that's not the reality ^^
Random rnd = new Random();
PictureBox Enemy = new PictureBox();
private async void timer1_Tick(object sender, EventArgs e)
{
if (Person.Bounds.IntersectsWith(Food.Bounds))
{
int ascii_index2 = rnd.Next(97, 123);
char char1 = Convert.ToChar(ascii_index2);
char char2 = Convert.ToChar(ascii_index2);
string myletter = Convert.ToString(char1) + Convert.ToString(char2);
Enemy = new PictureBox
{
Location = new Point(rnd.Next(20, playground.Right - 20), rnd.Next(20, playground.Bottom - 20)),
Name = "Enemy" + myletter,
Size = new Size(24,24),
BackColor = Color.Red,
};
this.Controls.Add(Enemy);
Enemy.BringToFront();
Food.Location = new Point(rnd.Next(20, playground.Right - 20), rnd.Next(20, playground.Bottom - 20));
score++;
lbScore.Text = "Score: " + score.ToString();
}
if(Person.Bounds.IntersectsWith(Enemy.Bounds))
{
timer1.Stop();
}
if (Person.Left <= playground.Left && !changed)
{
Person.Location = new Point(playground.Right, Person.Location.Y);
changed = true;
}
else
if (Person.Right >= playground.Right && !changed)
{
Person.Location = new Point(playground.Left, Person.Location.Y);
changed = true;
}
else
if (Person.Top <= playground.Top && !changed)
{
Person.Location = new Point(Person.Location.X, playground.Bottom);
changed = true;
}
else
if (Person.Bottom >= playground.Bottom && !changed)
{
Person.Location = new Point(Person.Location.X, playground.Top);
changed = true;
}
Here a picture from what i have : Player = BlackDot, Food = GreenDot, Enemy = RedDot
It moves auto in the direction you clicked and you can move faster if you hold the Key.
Move Keys: W,A,S,D
You need to create a list and store all the created enemies in there.
First you need to create a List:
List<PictureBox> enemyList = new List<PictureBox>();
and after you've created a new Enemy add it to your List
Enemy = new PictureBox{};
enemieList.add(Enemy);
Because then you can check for every PictureBox in this List and whether it Intersects with your Player or not.
foreach(PictureBox p in enemyList)
{
if(Person.Bounds.IntersectsWith(p.Bounds))
{
timer1.Stop();
}
}
That would be my fast solution to your problem.
Assuming that Person is your player, instead of having an enemy field of type PictureBox, have an enemies field of type List<PictureBox>. You then add each new enemy PictureBox to that list and then you can do this:
if (enemies.Any(enemy => person.Bounds.IntersectsWith(enemy.Bounds))

How to assign an arrow key to a button in C#?

right now I'm making a game and a character in it to move the player. I'm just a beginner about programming.
There are 8 buttons, and each button goes to a direction. For example, this my program for
private void btnUp_Click(object sender, EventArgs e)
{
//move up
y = y - 1;
MovePlayer();
UpdateLabelLocation();
}
public void MovePlayer()
{
picPlayer.Location = new Point(x, y);
}
public void UpdateLabelLocation()
{
lblLocation.Text = "Location: (" + x + ", " + y + ")";
}
I want to make it move when I press up, down, left or right keys. Also, if possible I want to make it so that when I press right and up at the same time, it triggers this:
private void btnRightUp_Click(object sender, EventArgs e)
{
//move player
y = y - 1;
x = x + 1;
MovePlayer();
UpdateLabelLocation();
}
I appreciate the help.
What you're essentially doing is creating your own simple game engine. As commentors have noted, you're better off using an existing game engine such as Unity. It's far easier and more liberating than going about it with WinForms.
That said, if you really want to continue with this, I strongly suggest you move the code in the Click handlers to a method. This reduces code duplication. i.e.
private void DownButton_Click(...)
{
MovePlayer(0, 1);
}
private void UpButton_Click(...)
{
MovePlayer(0, -1);
}
public void MovePlayer(float xStep, float yStep)
{
x += xStep;
y += yStep;
MovePlayer();
UpdateLabelLocation();
}
To move down and left you'd call MovePlayer(-1, 1);
To move up and right you'd call MovePlayer(1, -1);
Next you'll need to respond to KeyPress events. i.e.
public void Form_KeyPress(object sender, KeyPressEventArgs args)
{
switch (args.KeyChar) {
case 'a': // Left
args.Handled = true;
MovePlayer(-1, 0);
break;
case 'd': // Right
args.Handled = true;
MovePlayer(1, 0);
break;
case 'w': // Up
args.Handled = true;
MovePlayer(0, -1);
break;
case 's': // Down
args.Handled = true;
MovePlayer(0, 1);
break;
}
}
Note that if you have another control that accepts keyboard input(such as a TextBox), it will intercept the key press. To get around this, use KeyPreview to force the window to preview the input first. args.Handled = true prevents the event from routing to child controls after your code.
Unfortunately WinForms doesn't record multiple keys pressed at the same time, so using KeyPress alone isn't enough to handle corner movement. You can work around this by hooking onto KeyDown and KeyUp, but it's more trouble than it's worth.
Here's a more robust solution. Bear in mind the following isn't thread safe, so if you plan on introducing other threads you'll need to use appropriate locking.
HashSet<KeyCode> state = new HashSet<KeyCode>();
float speed = 120; // 120 pixels/second.
private void Form_KeyDown(object sender, KeyEventArgs args)
{
var key = args.KeyCode;
state.Add(key);
// Fire pressed when a key was up.
if (!state.Contains(key)) {
state.Add(key);
OnKeyPressed(key);
}
}
private void Form_KeyUp(object sender, KeyEventArgs args)
{
var key = args.KeyCode;
state.Remove(key);
// Fire release when a key was down.
if (state.Contains(key)) {
state.Remove(key);
OnKeyReleased(key);
}
}
// Runs when key was up, but pressed just now.
private void OnKeyPressed(KeyCode key)
{
// Trigger key-based actions.
}
// Runs when key was down, but released just now.
private void OnReleased(KeyCode key)
{
// Trigger key-based actions, but on release instead of press.
}
private bool IsDown(KeyCode key)
{
return state.Contains(key);
}
// Trigger this periodically, at least 20 times a second(ideally 60).
// An option to get you started is to use a windows timer, but
// eventually you'll want to use high precision timing instead.
private void Update()
{
var deltaTime = // Calculate the seconds that have passed since the last update.
// Describing it is out of the scope of this answer, but see the links below.
// Determine horizontal direction. Holding both
// A & D down cancels movement on the x-axis.
var directionX = 0;
if (IsDown(KeyCode.A)) {
directionX--;
}
if (IsDown(KeyCode.D)) {
directionX++;
}
// Determine vertical direction. Holding both
// W & S down cancels movement on the y-axis.
var directionY = 0;
if (IsDown(KeyCode.W)) {
directionY--;
}
if (IsDown(KeyCode.S)) {
directionY++;
}
// directionX & directionY should be normalized, but
// I leave that as an exercise for the reader.
var movement = speed * deltaTime;
var offsetX = directionX * movement;
var offsetY = directionY * movement;
MovePlayer(offsetX, offsetY);
}
As you can see, there's quite a bit involved. If you want more fine-grained timing, look into this article. Eventually you'll want to transition to a game loop, but that's yet another topic out of the scope of this answer.

Public void int increment by 1 every loop (Beginner)

I'd basically like to know how I can increase Player.SetPoints(); by 1 every time it loops. I got this in my class public void SetPoints(int p) { points = p; }, just thought that would be worth mentioning. So far the only way I've gotten this to work is to use Player.SetPoints(+1); but that only puts 1 to the scoreboard and never increases again. Sorry If I'm not clear enough, I'm very new to programming. This is a sample of my code so you might be able to understand it a bit more, I'm making a tic tac toe game as an assignment for school.
if (Btn1.Text != "" && Btn2.Text != "" && Btn3.Text != "")
{
if (Btn1.Text == Btn2.Text && Btn1.Text == Btn3.Text)
{
Btn1.BackColor = Color.Green;
Btn1.ForeColor = Color.White;
Btn2.BackColor = Color.Green;
Btn2.ForeColor = Color.White;
Btn3.BackColor = Color.Green;
Btn3.ForeColor = Color.White;
if (Btn1.Text == "X")
{
MessageBox.Show(lblpl1.Text + " Has Won!");
Player1.SetPoints(+0);
playerscore1.Text = Player1.GetPoints().ToString();
}
else
{
MessageBox.Show(lblpl2.Text + " Has Won!");
Player2.SetPoints(+0);
playerscore2.Text = Player2.GetPoints().ToString();
}
click1++;
click2++;
click3++;
click4++;
click5++;
click6++;
click7++;
click8++;
click9++;
restartbtn.Visible = true;
}
}
In order increase something in each button click you need to save the previous value in a variable. You are doing a similar think with clicks:
click1++;
Your method sets the points to a given value. If you give it 1 it sets it to 1. It looks like you don't want to set points to an individual value, you want to increase it each time, so instead of declaring a method like that you can change it to:
public void IncreasePoints()
{
points++;
}
And simply call it without passing any value.
Player1.IncreasePoints();

Head First C# method issue

I have this weird issue occuring everytime I run my game "Day at the Races" in Visual Studio. So basically I'm working with Forms. I have 4 PictureBoxes, with dog photos. Dogs info are stored in an array. After each race I want to put these Pictures back on the starting line. So I have made a method:
public void SetToStartingPosition()
{
for (int j=0; j < GreyhoundArray.Length; j++)
{
GreyhoundArray[j].MyPictureBox.Left = 0;
}
}
I run this method every time the race is finished and here's what happens:
All 4 PictureBoxes goes back to starting position (x=0) as intended, then the MessageBox pops up, and when I press "OK" to close it, a random number from 1 to 3 of PictureBoxes(Dogs) goes back to the right position(x=600)... I just have no idea why closing this MessageBox has such weird influence, I don't see any connection between these two, also no other methods have anything to do with PictureBox.Left. Any ideas of what's happening would be very appreciated.
public void timer1_Tick(object sender, EventArgs e)
{
for (int i = 0; i <GreyhoundArray.Length; i++)
{
GreyhoundArray[i].Run(); //method making PictureBoxes move to the right
if (GreyhoundArray[i].Run() == true) //GreyhoundArray[i].Run() is true when the race is finished
{
timer1.Stop();
**SetToStartingPosition();**
winDog = i;
MessageBox.Show("Dog number " + (winDog + 1) + " has won!");
groupBox1.Enabled = true;
button1.Text = "RESTART!";
for (int k =0; k < GuysArray.Length; k++)
{
if (GuysArray[k].MyBet != null)
{
GuysArray[k].Collect(winDog);
GuysArray[k].ClearBet();
GuysArray[k].UpdateLabels();
}
}
break;
}
}
}
Edit:
Oh wow, you just gave ma a clue whats wrong, so there actually was PictureBox location changing line in Run() method, which I was sure would stop when one of the dogs win the race, but I didnt break the loop, so it was still going, and setting dogs back on their position. All I had to do was adding 'break;' in the end of the loop. And just for your and some others info:
` public bool Run()
{
if (MyPictureBox.Left > 600 - MyPictureBox.Width)
{
return true;
}
else
{
Location += MyRandom.Next(1, 4);
MyPictureBox.Left = StartingPosition + Location;
return false;
} `
Thanks for help tho, thats a bad and ambarrasing start of my Stack Overflow "carrer" :(

Dart Scoring Game Using C# Visual Studio 2010 - Alternating player score after evey 3rd score

Just bought a dartboard and thought to create a little scoring application, so far i have set up the player names and individual buttons to start the game. three games - 301, 501 and 1001, these are the targets scores to get to zero by throwing three darts each in turn by two players.
I have included many buttons for the score of each dart from 20 down to 1, each for single, double, triple, bull, outer bull and a no score button. Once the games starts the first three button presses should be attributed to player one then the following three buttons pressed to allocate the corresponding score to player 2. The games ends with the winning player achieving the target score (or whittles it down to zero).
I could have a player selection button to do this but was after some tips on a way of coding the alternate pattern of scoring to be automatic.
Any help greatly appreciated. Thank you
namespace dbstats
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
btnPlay.Visible = false;
btnReset.Visible = false;
btnUndo.Visible = false;
gbDartBoard.Visible = false;
gbScoreBoard.Visible = false;
lbP1Select.Visible = false;;
lbP2Select.Visible = false;
cmbP1.Visible = false;
cmbP2.Visible = false;
string[] lines = File.ReadAllLines(#"playerStats.csv");
foreach (var line in lines)
{
string[] names = line.Split(',');
if (names[0] != "NAME")
{
cmbP1.Items.Add(names[0]);
cmbP2.Items.Add(names[0]);
}
}
}
private void tsm301_Click(object sender, EventArgs e)
{
lbP1Select.Visible = true;
cmbP1.Visible = true;
lbP2Select.Visible = true;
cmbP2.Visible = true;
btnPlay.Visible = true;
tbPlayer1.Text = "301";
tbPlayer2.Text = "301";
gamesToolStripMenuItem.Visible = false;
manageToolStripMenuItem.Visible = false;
}
private void btnPlay_Click(object sender, EventArgs e)
{
if (cmbP1.SelectedItem == cmbP2.SelectedItem || cmbP1.SelectedItem == null || cmbP2.SelectedItem == null)
{
MessageBox.Show("Make Sure:" + "\n\n"
+ "The Players are NOT the same." + "\n"
+ "&" + "\n"
+ "At least one selection is NOT left blank", "Choose Again!");
}
else
{
lbP1Select.Visible = false;
cmbP1.Visible = false;
lbP2Select.Visible = false;
cmbP2.Visible = false;
btnPlay.Visible = false;
lbPlayer1.Text = cmbP1.SelectedItem.ToString();
lbPlayer2.Text = cmbP2.SelectedItem.ToString();
btnReset.Visible = true;
btnUndo.Visible = true;
gbDartBoard.Visible = true;
gbScoreBoard.Visible = true;
lbPlayer1.Visible = true; ;
lbPlayer2.Visible = true;
}
}
}
Keep a turnsEntered member variable to whatever class is managing turns/scoring.
// Call after every time a new score is entered. Start at 0.
turnsEntered++;
if (turnsEntered % 3 == 0)
{
SwitchPlayer(); // However you keep track of current player - switch here
}
After the 3rd score is entered it will switch players...after 3 more it will switch again, etc...
You can later check turnsEntered to determine when the game is over. Set it back to zero when you reinitialize for a new game.

Categories