Крестики нолики на си шарп windows form

Крестики нолики C#, .NET 4.x Решение и ответ на вопрос 1532248
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
using System;
using System.Drawing;
using System.Net;
using System.Windows.Forms;
 
namespace WindowsFormsApplication309
{
    public partial class Form1 : Form
    {
        private Game game;
        private PictureBox[,] pbs = new PictureBox[3,3];
        private Image Cross;
        private Image Nought;
 
        public Form1()
        {
            InitializeComponent();
 
            Init();
 
            game = new Game();
            Build(game);
        }
 
        void Init()
        {
            Cross = Image.FromStream(new WebClient().OpenRead("https://raw.github.com/christkv/tic-tac-toe/master/public/img/cross.png"));
            Nought = Image.FromStream(new WebClient().OpenRead("https://raw.github.com/christkv/tic-tac-toe/master/public/img/circle.png"));
 
            for (int i = 0; i < 3; i++)
            for (int j = 0; j < 3; j++)
            {
                pbs[i, j] = new PictureBox { Parent = this, Size = new Size(100, 100), Top = i * 100, Left = j * 100, BorderStyle = BorderStyle.FixedSingle, Tag = new Point(i, j), Cursor = Cursors.Hand, SizeMode = PictureBoxSizeMode.StretchImage };
                pbs[i, j].Click += pb_Click;
            }
 
            new Button { Parent = this, Top = 320, Text = "Reset" }.Click += delegate { game = new Game(); Build(game); };
        }
 
        private void Build(Game game)
        {
            for (int i = 0; i < 3; i++)
            for (int j = 0; j < 3; j++)
                pbs[i, j].Image = game.Items[i, j] == FieldState.Cross ? Cross : (game.Items[i, j] == FieldState.Nought ? Nought : null);
        }
 
        void pb_Click(object sender, EventArgs e)
        {
            game.MakeMove((Point) (sender as Control).Tag);
            Build(game);
 
            if(game.Winned)
                MessageBox.Show(string.Format("{0} is winner!", game.CurrentPlayer == 0 ? "Cross" : "Nought"));
        }
    }
 
    class Game
    {
        public FieldState[,] Items = new FieldState[3,3];
        public int CurrentPlayer = 0;
        public bool Winned;
 
        public void MakeMove(Point p)
        {
            if (Items[p.X, p.Y] != FieldState.Empty)
                return;
 
            if (Winned)
                return;
 
            Items[p.X, p.Y] = CurrentPlayer == 0 ? FieldState.Cross : FieldState.Nought;
            if (CheckWinner(FieldState.Cross) || CheckWinner(FieldState.Nought))
            {
                Winned = true;
                return;
            }
 
            CurrentPlayer ^= 1;
        }
 
        private bool CheckWinner(FieldState state)
        {
            for (int i = 0; i < 3; i++)
            {
                if (Items[i, 0] == state && Items[i, 1] == state && Items[i, 2] == state)
                    return true;
                if (Items[0, i] == state && Items[1, i] == state && Items[2, i] == state)
                    return true;
            }
 
            if (Items[0, 0] == state && Items[1, 1] == state && Items[2, 2] == state)
                return true;
 
            if (Items[0, 2] == state && Items[1, 1] == state && Items[2, 0] == state)
                return true;
 
            return false;
        }
    }
 
    enum FieldState
    {
        Empty,
        Cross,
        Nought
    }
}

So I suppose the answer to your question can go in a few different routes. Best practices would suggest that you write tests, encapsulate and use some different design patterns. However my thoughts when looking at your code is that this might be a little frustrating to you. But your question also points to something that I think a few people will miss. Making 10 small classes to solve the same problem (in terms of lines of code) will be the same as 1 large class. I’ll offer this suggestion as a rebuttal and an answer. Learn how to encapsulate in clear logical manners. You’ll end up with 10 (just a random number) small classes. The point being that the smaller the class the easier it is to understand what it does.

To answer your question as a superficial answer would be to make methods to remove the duplicate code. Your winners method has TONS of duplicate code and would be greatly simplified like so

public bool winners()
{
    bool winner = false;
    winner |= TestForWinner(player, 0,1,2, "X is Winner", Color.Blue);
    winner |= TestForWinner(player, 3,4,5, "X is Winner", Color.Blue);
    winner |= TestForWinner(player, 6,7,8, "X is Winner", Color.Blue);
    winner |= TestForWinner(player, 0,4,8, "X is Winner", Color.Blue);
    winner |= TestForWinner(player, 2,4,6, "X is Winner", Color.Blue);
    winner |= TestForWinner(player, 0,3,6, "X is Winner", Color.Blue);
    winner |= TestForWinner(player, 2,5,8, "X is Winner", Color.Blue);
    winner |= TestForWinner(cpu, 0,1,2, "0 is Winner", Color.Red);
    winner |= TestForWinner(cpu, 3,4,5, "0 is Winner", Color.Red);
    winner |= TestForWinner(cpu, 6,7,8, "0 is Winner", Color.Red);
    winner |= TestForWinner(cpu, 0,4,8, "0 is Winner", Color.Red);
    winner |= TestForWinner(cpu, 2,4,6, "0 is Winner", Color.Red);
    winner |= TestForWinner(cpu, 0,3,6, "0 is Winner", Color.Red);
    winner |= TestForWinner(cpu, 2,5,8, "0 is Winner", Color.Red);

    return winner;
}
private boolean TestForWinner(string test, int index1, int index2, int index3, string winnerText, Color winningColor)
{
    if(buttons[index1] == test && buttons[index2] == test && buttons[index3] == test)
    {
        buttons[index1].BackColor = winningColor;
        buttons[index2].BackColor = winningColor;
        buttons[index3].BackColor = winningColor;

        MessageBox.Show(winnerText);
        winnerrest();
        return true;
    }
    return false;
}

out of curiosity I double checked the work and you missed two scenarios. A win in column 2 and a draw. My solution looks like this

    private bool HasWinner()
    {
        foreach (var player in players)
        {
            if (CheckForWinner(player, 0, 1, 2)) return true;//win row 1
            else if (CheckForWinner(player, 3, 4, 5)) return true;// win row 2
            else if (CheckForWinner(player, 6, 7, 8)) return true;// win row 3
            else if (CheckForWinner(player, 0, 3, 6)) return true;// win column 1
            else if (CheckForWinner(player, 1, 4, 7)) return true;// win column 2 <--missed this one you did
            else if (CheckForWinner(player, 2, 5, 8)) return true;// win column 3
            else if (CheckForWinner(player, 0, 4, 8)) return true;// win 
            else if (CheckForWinner(player, 2, 4, 6)) return true;// win /
        }
        if (!buttons.Any(x => x.Enabled))
        {
            MessageBox.Show("Cats Game");
            return true;
        }
        return false;
    }

I made a Player class which has a string called Marker, and a Color called WinningColor
I have an array of Player and switch back and forth which one is current. This allows me to loop through each player checking for a win, and if no moves are left showing the user it’s a draw and returning true. Might be worth trying to replicate what I did.

So I suppose the answer to your question can go in a few different routes. Best practices would suggest that you write tests, encapsulate and use some different design patterns. However my thoughts when looking at your code is that this might be a little frustrating to you. But your question also points to something that I think a few people will miss. Making 10 small classes to solve the same problem (in terms of lines of code) will be the same as 1 large class. I’ll offer this suggestion as a rebuttal and an answer. Learn how to encapsulate in clear logical manners. You’ll end up with 10 (just a random number) small classes. The point being that the smaller the class the easier it is to understand what it does.

To answer your question as a superficial answer would be to make methods to remove the duplicate code. Your winners method has TONS of duplicate code and would be greatly simplified like so

public bool winners()
{
    bool winner = false;
    winner |= TestForWinner(player, 0,1,2, "X is Winner", Color.Blue);
    winner |= TestForWinner(player, 3,4,5, "X is Winner", Color.Blue);
    winner |= TestForWinner(player, 6,7,8, "X is Winner", Color.Blue);
    winner |= TestForWinner(player, 0,4,8, "X is Winner", Color.Blue);
    winner |= TestForWinner(player, 2,4,6, "X is Winner", Color.Blue);
    winner |= TestForWinner(player, 0,3,6, "X is Winner", Color.Blue);
    winner |= TestForWinner(player, 2,5,8, "X is Winner", Color.Blue);
    winner |= TestForWinner(cpu, 0,1,2, "0 is Winner", Color.Red);
    winner |= TestForWinner(cpu, 3,4,5, "0 is Winner", Color.Red);
    winner |= TestForWinner(cpu, 6,7,8, "0 is Winner", Color.Red);
    winner |= TestForWinner(cpu, 0,4,8, "0 is Winner", Color.Red);
    winner |= TestForWinner(cpu, 2,4,6, "0 is Winner", Color.Red);
    winner |= TestForWinner(cpu, 0,3,6, "0 is Winner", Color.Red);
    winner |= TestForWinner(cpu, 2,5,8, "0 is Winner", Color.Red);

    return winner;
}
private boolean TestForWinner(string test, int index1, int index2, int index3, string winnerText, Color winningColor)
{
    if(buttons[index1] == test && buttons[index2] == test && buttons[index3] == test)
    {
        buttons[index1].BackColor = winningColor;
        buttons[index2].BackColor = winningColor;
        buttons[index3].BackColor = winningColor;

        MessageBox.Show(winnerText);
        winnerrest();
        return true;
    }
    return false;
}

out of curiosity I double checked the work and you missed two scenarios. A win in column 2 and a draw. My solution looks like this

    private bool HasWinner()
    {
        foreach (var player in players)
        {
            if (CheckForWinner(player, 0, 1, 2)) return true;//win row 1
            else if (CheckForWinner(player, 3, 4, 5)) return true;// win row 2
            else if (CheckForWinner(player, 6, 7, 8)) return true;// win row 3
            else if (CheckForWinner(player, 0, 3, 6)) return true;// win column 1
            else if (CheckForWinner(player, 1, 4, 7)) return true;// win column 2 <--missed this one you did
            else if (CheckForWinner(player, 2, 5, 8)) return true;// win column 3
            else if (CheckForWinner(player, 0, 4, 8)) return true;// win 
            else if (CheckForWinner(player, 2, 4, 6)) return true;// win /
        }
        if (!buttons.Any(x => x.Enabled))
        {
            MessageBox.Show("Cats Game");
            return true;
        }
        return false;
    }

I made a Player class which has a string called Marker, and a Color called WinningColor
I have an array of Player and switch back and forth which one is current. This allows me to loop through each player checking for a win, and if no moves are left showing the user it’s a draw and returning true. Might be worth trying to replicate what I did.

крестики нолики код на c#

Доброго времени суток. На данной странице я приведу пример создания всеми любимой логической игры под названием «крестики-нолики». Так как программирую я в основном на C#, то и представленная программа будет так же на данном языке программирования. Но даже если Вы не знакомы с C#, а программируете на C++, Java, Delphi или на любом другом языке, данный пост в любом случае поможет Вам, так как тут я приведу алгоритм работы такой программы.

Рассматривать программу, предназначенную для игры вдвоем, мы не будем. Сейчас мы рассмотрим программу, в которой противником пользователя будет компьютер, ведь для программа для игры двоих будет состоять только из необходимости прорисовки ходов, что, думаю, довольно просто. Поэтому мы рассмотрим игру компьютера.

Всю программу можно поделить на две большие части:

  1. Компьютер ходит первым(крестики)
  2. Компьютер ходит вторым(нолики)

Далее я опишу стратегию игры компьютера, которого мы собираемся сделать непобедимым.

Стратегия игры «крестики-нолики»

Основополагающие правила

Независимо от того, кто ходит первым и какая сейчас позиция, компьютер перед каждый ходом должен проверить две вещи:

  1. Можно ли выиграть? Если да, то компьютер выигрывает!
  2. Угрожает ли нам опасность проиграть следующим ходом? Если да, то закрываем такую возможность!

Компьютер ходит первым

Компьютер свой первый ход делает в центр. Тут другого варианта нет. Дальше ходит пользователь, у которого есть только два варианта: либо сходить в угол, либо сходить на сторону. Если пользователь ходит в угол, то компьютер отвечает ходом в противоположный угол. Если же пользователь ходит на сторону, то компьютер должен сходить в один из углов, которые расположены дальше всего от хода пользователя. Таких углов, если Вы понимаете о чем я, всего два. Разницы в какой из них сходить нет. Если эти противоположные углы заняты, компьютер ходит в любое свободное место(это условие актуально и если пользователь сходил в угол, и если он сходил на сторону). Данное правило ходить как можно дальше от предыдущего хода соперника актуально до конца игры, в которой компьютер сходил первым.

Компьютер ходит вторым

Когда компьютер ходит вторым, а пользователь, соответственно, первым, может возникнуть несколько ситуаций:

  1. Первый ход в центр. Тогда ходим до конца игры в любой из углов, а если это невозможно, то в любую свободную клетку.
  2. Первый ход в угол. Отвечаем ходом в центр. Следующие ходы — в любой из свободных углов, иначе — в свободные клетки.
  3. Первый ход в сторону. Ситуация аналогичная предыдущей: по максимуму закрываем углы, после чего ходим туда, где осталось место.

Вот такая стратегия игры в «крестики-нолики». Она не слишком сложная, и теперь мы попытаемся ее реализовать.

Реализация программы «крестики-нолики»

Предлагать Вам полный код всей программы я не буду, все же лучше чему то научится самим. Но основной костяк программы я вам предложу. Собирать же воедино останется Вам. Итак, начнем мы с основополагающих правил. Надеюсь, вы не забыли, что эти правила должны проверяться перед каждый ходом.

Проверка на возможность победы

Перед тем как писать код, необходимо уяснить смысл ее работы. Выигрышных комбинаций всего 8. Можете посмотреть скриншоты: я специально выделил и указал номер этих комбинаций(они понадобятся нам). Эти номера комбинаций будет хранить переменная cherta, которая сразу после выигрыша отдаст команду нарисовать выигрышную черту.

крестики нолики
крестики нолики
крестики нолики
Мы должны проверить каждую из этих комбинаций на предмет того, стоят ли там две наши крестики(или нолики), а так же свободно ли третья клетка. Но перед тем как мы перейдем к этому, я немного познакомлю вас с другой основой программы.

Для хранения ходов как компьютера, так и пользователя прекрасно подходит обычная матрица 3×3, в которой можно указывать специальный номер ходившей стороны. Для своей игры я задал такие значения:

  • Изначально матрица обнуляется и это значит, что все 9 клеток пусты. Перед каждый ходом нужно проверять, пустая ли клетка, и можно ли на нее сходить. Полезно, пользуйтесь.
  • Для обозначения компьютера я использовал цифру 1.
  • Для обозначения пользователя я использовал цифру 2.

Далее нам необходимо будет создать специальный метод, который будет отрисовывать игровое поле, используя данные этой матрицы(метод paint, который будет мелькать в коде). А вот собственно и код данного метода:

[code language=»csharp»]if (((a[0, 0] + a[0, 1] + a[0, 2]) == 2) && (a[0, 0] == 1 || a[0, 1] == 1 || a[0, 2] == 1))
{
for (int j = 0; j < 3; j++)
{
if (a[0, j] == 0)
{
a[0, j] = 1;
}
}
win = 1;
paint();
cherta = 4;
}
else
{
if (((a[1, 0] + a[1, 1] + a[1, 2]) == 2) && (a[1, 0] == 1 || a[1, 1] == 1 || a[1, 2] == 1))
{
for (int j = 0; j < 3; j++)
{
if (a[1, j] == 0)
{
a[1, j] = 1;
}
}
win = 1;
paint();
cherta = 5;
}
else
{
if (((a[2, 0] + a[2, 1] + a[2, 2]) == 2) && (a[2, 0] == 1 || a[2, 1] == 1 || a[2, 2] == 1))
{
for (int j = 0; j < 3; j++)
{
if (a[2, j] == 0)
{
a[2, j] = 1;
}
}
win = 1;
paint();
cherta = 6;
}
else
{
if (((a[0, 0] + a[1, 0] + a[2, 0]) == 2) && (a[0, 0] == 1 || a[1, 0] == 1 || a[2, 0] == 1))
{
for (int i = 0; i < 3; i++)
{
if (a[i, 0] == 0)
{
a[i, 0] = 1;
}
}
win = 1;
paint();
cherta = 1;
}
else
{
if (((a[0, 1] + a[1, 1] + a[2, 1]) == 2) && (a[0, 1] == 1 || a[1, 1] == 1 || a[2, 1] == 1))
{
for (int i = 0; i < 3; i++)
{
if (a[i, 1] == 0)
{
a[i, 1] = 1;
}
}
win = 1;
paint();
cherta = 2;
}
else
{
if (((a[0, 2] + a[1, 2] + a[2, 2]) == 2) && (a[0, 2] == 1 || a[1, 2] == 1 || a[2, 2] == 1))
{
for (int i = 0; i < 3; i++)
{
if (a[i, 2] == 0)
{
a[i, 2] = 1;
}
}
win = 1;
paint();
cherta = 3;
}
else
{
if (((a[0, 0] + a[1, 1] + a[2, 2]) == 2) && (a[0, 0] == 1 || a[1, 1] == 1 || a[2, 2] == 1))
{
if (a[0, 0] == 0)
a[0, 0] = 1;
if (a[1, 1] == 0)
a[1, 1] = 1;
if (a[2, 2] == 0)
a[2, 2] = 1;
win = 1;
paint();
cherta = 7;
}
else
{
if (((a[2, 0] + a[1, 1] + a[0, 2]) == 2) && (a[2, 0] == 1 || a[1, 1] == 1 || a[0, 2] == 1))
{
if (a[2, 0] == 0)
a[2, 0] = 1;
if (a[1, 1] == 0)
a[1, 1] = 1;
if (a[0, 2] == 0)
a[0, 2] = 1;
win = 1;
paint();
cherta = 8;
}
}
}
}
}
}
}
}[/code]

Тут так же появляется переменная win, которая хранит значение победившей стороны. Я использовал только два значения: 0-ничья, 1 — победа компьютера. В моей программе нет даже поздравления пользователя с возможной победой, так как это невозможно.

Проверка на возможность проигрыша следующим ходом

[code language=»csharp»]if ((a[0, 0] + a[0, 1] + a[0, 2]) == 4 && a[0, 0]!=1 && a[0, 1]!=1 && a[0, 2]!=1) //1-4-7 — защита
{
for (int j = 0; j < 3; j++)
{
if (a[0, j] == 0)
{
a[0, j] = 1;
hdpc = false;
paint();
}
}
}
else
{
if ((a[1, 0] + a[1, 1] + a[1, 2]) == 4 && a[1, 0] != 1 && a[1, 1] != 1 && a[1, 2] != 1) //2-5-8 — защита
{
for (int j = 0; j < 3; j++)
{
if (a[1, j] == 0)
{
a[1, j] = 1;
hdpc = false;
paint();
}
}
}
else
{
if ((a[2, 0] + a[2, 1] + a[2, 2]) == 4 && a[2, 0] != 1 && a[2, 1] != 1 && a[2, 2] != 1) //3-6-9 — защита
{
for (int j = 0; j < 3; j++)
{
if (a[2, j] == 0)
{
a[2, j] = 1;
hdpc = false;
paint();
}
}
}
else
{
if ((a[0, 0] + a[1, 0] + a[2, 0]) == 4 && a[0, 0] != 1 && a[1, 0] != 1 && a[2, 0] != 1) //1-2-3 — защита
{
for (int i = 0; i < 3; i++)
{
if (a[i, 0] == 0)
{
a[i, 0] = 1;
hdpc = false;
paint();
}
}
}
else
{
if ((a[0, 1] + a[1, 1] + a[2, 1]) == 4 && a[0, 1] != 1 && a[1, 1] != 1 && a[2, 1] != 1) //4-5-6 — защита
{
for (int i = 0; i < 3; i++)
{
if (a[i, 1] == 0)
{
a[i, 1] = 1;
hdpc = false;
paint();
}
}
}
else
{
if ((a[0, 2] + a[1, 2] + a[2, 2]) == 4 && a[0, 2] != 1 && a[1, 2] != 1 && a[2, 2] != 1) //7-8-9 — защита
{
for (int i = 0; i < 3; i++)
{
if (a[i, 2] == 0)
{
a[i, 2] = 1;
hdpc = false;
paint();
}
}
}
else
{
if ((a[0, 0] + a[1, 1] + a[2, 2]) == 4 && a[0, 0] != 1 && a[1, 1] != 1 && a[2, 2] != 1) //1-5-9 — защита
{
if (a[0, 0] == 0)
a[0, 0] = 1;
if (a[1, 1] == 0)
a[1, 1] = 1;
if (a[2, 2] == 0)
a[2, 2] = 1;
hdpc = false;
paint();
}
else
{
if ((a[2, 0] + a[1, 1] + a[0, 2]) == 4 && a[2, 0] != 1 && a[1, 1] != 1 && a[0, 2] != 1) //3-5-7 — защита
{
if (a[2, 0] == 0)
a[2, 0] = 1;
if (a[1, 1] == 0)
a[1, 1] = 1;
if (a[0, 2] == 0)
a[0, 2] = 1;
hdpc = false;
paint();
}
}
}
}
}
}
}
}[/code]

Как видите, смысл защиты от возможного поражения является почти что точной копией предыдущего метода. Тут стоит лишь напомнить и уточнить несколько вещей:

  • Ходы противника мы отмечаем значением 2. Для этого используется массив a.
  • Метод paint вызывает отрисовку на основе данных матрицы.
  • Переменная булевого типа hdpc(hod pc, ход компьютера) принимает значение false, если пришла очередь ходить пользователю.

Дирижер всех ходов компьютера

Я использовал несколько переменных, которые сохраняли некоторое значение, на основании которых можно было бы понять, чей сейчас ход и кто ходил первым. Очень важным моментом всей программы является необходимость передавать управление то пользователю, то компьютеру. Для этого я рекомендую простой способ: лично я использовал элемент panel, который выполняет роль игрового поле. С помощью булевой переменной hdpc, я либо разрешал принимать нажатие мышкой пользователя на panel, либо нет, что обеспечивало только своевременный ход пользователя. Так же сразу после хода пользователя я запускал на действие специальный метод, который я образно назвал дирижером всех ходом компьютера. В чем это определение очень даже подходит для него, ведь именно здесь будут приниматься решения, какой метод активировать, то есть именно тут можно увидеть всю стратегию данной игры:

[code language=»csharp»]nichia(); //проверяем, если хотя бы одно свободное место на поле. Если да, то ходим. Если нет, то вывод сообщения о ничье. Данный метод предлагаю написать вам самим, он довольно прост.
pobeda(); //это тот самый метод для проверки возможности выигрыша
if (win == 0) //как помните мы использовали переменную win, чтобы обозначить победу компьютера. Если компьютер еще не выиграл, то продолжаем
{
zachita(); //этот метод мы так же описали. Компьютер защищается от возможного проигрыша
if (hdpc == true) //до сих пор ход компьютера, а это значит компьютер пока не может выиграть и поражением никто не пугает, а значит можно ходить по стратегии
{
if (pc == 1) //данная переменная хранит информацию о том, кто первым ходил. Если первым ходил компьютер, то значение равно 1, если пользователь — то 2.
{
krestiki(); //так называется метод, который выполняет ход в самый отдаленный угол от предыдущего хода пользователя
}
else //компьютер ходит вторым
{
if (xfir == 1 && yfir == 1) //пользователь первым ходом сходил в центр
{
ugol(); //ищем любой свободный угол и ходим туда, если это невозможно, ходим в любую свободную клетку
}
else //пользователь первым ходом не сходил в центр
{
if (pervhod) //данная булевая переменная хранит true, если пользователь собирается сделать первый ход в этой партии
{
hod1(); //ходим в центр
pervhod = false;
}
else
{
ugol(); //ходим в свободный угол
}
}
}
} //конец противоположного хода
nichia();
}
else
{
winner();
}[/code]

В данной методе, который я заботливо уложил комментариями, и хранится вся стратегия игры в «крестики-нолики». Нам же остается дописать все необходимые методы.

Ход в противоположный от предыдущего хода угол

Данный метод, который в представленном коде был назван как krestiki, выполняет ход в противоположный от предыдущего шага пользователя угол. Чтобы выполнить данную операцию, мы должны знать, куда последним шагом сходил пользователь. Именно для этих целей я использую специальные переменные. Парочку из них(xfir и yfir — сократил от x first и y first) я уже использовал в предыдущем методе. Данные переменный запоминают только первый ход пользователя. Следующие переменные, которые вы встретите, будут хранить каждый последний шаг пользователя.

[code language=»csharp»]if (xlast == 0 && ylast == 0) //если 0,0
{
if (a[2, 2] == 0)
{
a[2, 2] = 1;
hdpc = false;
paint();
}
else
{
random();
}
}
else
{
if (xlast == 2 && ylast == 0) //2.0
{
if (a[0, 2] == 0)
{
a[0, 2] = 1;
hdpc = false;
paint();
}
else
{
random();
}
}
else
{
if (xlast == 0 && ylast == 2) //0.2
{
if (a[2, 0] == 0)
{
a[2, 0] = 1;
hdpc = false;
paint();
}
else
{
random();
}
}
else
{
if (xlast == 2 && ylast == 2) //2.2
{
if (a[0, 0] == 0)
{
a[0, 0] = 1;
hdpc = false;
paint();
}
else
{
random();
}
}
else
{
if (xlast == 0 && ylast == 1) //0.1
{
if (a[2, 0] == 0)
{
a[2, 0] = 1;
hdpc = false;
paint();
}
else
{
if (a[2, 2] == 0)
{
a[2, 2] = 1;
hdpc = false;
paint();
}
else
{
random();
}
}
}
else
{
if (xlast == 1 && ylast == 0) //1.0
{
if (a[0, 2] == 0)
{
a[0, 2] = 1;
hdpc = false;
paint();
}
else
{
if (a[2, 2] == 0)
{
a[2, 2] = 1;
hdpc = false;
paint();
}
else
{
random();
}
}
}
else
{
if (xlast == 2 && ylast == 1) //2.1
{
if (a[0, 0] == 0)
{
a[0, 0] = 1;
hdpc = false;
paint();
}
else
{
if (a[0, 2] == 0)
{
a[0, 2] = 1;
hdpc = false;
paint();
}
else
{
random();
}
}
}
else
{
if (xlast == 1 && ylast == 2) //1.2
{
if (a[0, 0] == 0)
{
a[0, 0] = 1;
hdpc = false;
paint();
}
else
{
if (a[2, 0] == 0)
{
a[2, 0] = 1;
hdpc = false;
paint();
}
else
{
random();
}
}
}
}
}
}
}
}
}
}[/code]

В данном методе мы знакомимся с новым методом random, который отвечает, надеюсь вы догадались, за абсолютно рандомный(случайный шаг). Помните, в стратегии есть и такие ходы. Данный метод я так же оставлю для реализации Вам.

Ход в любой из углов

Последний метод, который я опишу в данной статье, которая неимоверно растянулась, это ход в любой из пустых углов, если конечно такая возможность имеется. Любуйтесь!

[code language=»csharp»]if (a[0, 0] == 0)
{
a[0, 0] = 1;
hdpc = false;
paint();
}
else
{
if (a[2, 0] == 0)
{
a[2, 0] = 1;
hdpc = false;
paint();
}
else
{
if (a[0, 2] == 0)
{
a[0, 2] = 1;
hdpc = false;
paint();
}
else
{
if (a[2, 2] == 0)
{
a[2, 2] = 1;
hdpc = false;
paint();
}
else
{
random();
}
}
}
}[/code]

Данный метод сделан на основе предыдущего метода, разница лишь в том, что в нем отсутствуют лишние проверки. Тут все просто: находим пустой угол — ходим, не находим — ходим рандомно.

Я понимаю, что приведенная часть это только половина всей программы. Но это основная половина всей программы. В ближайшее время я напишу еще одну статью, в которой опишу «рисовательную» часть этой программы, а так же укажу как отловить ход пользователя на panel`и. Напомню, что все это будет на языке C#. Так же напомню, что мы только что писали код программы, которая имитирует игру компьютера с пользователем в «Крестики-нолики»

Update: Боюсь у меня нет времени и еще больше нет желания заново разбираться в игре, которую я некогда написал и поэтому, скорее всего, обещанная рисовательная часть программы и прочие фишки никогда не увидят свет на страницах сайта About-windows.ru. В связи с этим просто предлагаю вам скачать исходник этой игры и дальше уже самим разобраться в ее коде. Прошу не судить строго по поводу «красоты» программирования.

Ссылка на скачивание игры Крестики и нолики.

P.S. Для тех кто заявляет, что программу можно выиграть, просьба предоставить доказательства(скриншот; порядок ходов с указанием того, кто начинает партию). При этом не нужно обременять себя рассказами про Ваши геройства, если вы играли нечестно(защита от дурака и хакера не продумана так же хорошо, так алгоритм программы).

Если кратко, то вам просто нужно перед тем как ставить нолики, сначала проверять нет ли там чего другого (ифом простым).
а если долго, то возможно более понятным и правильным способом решения этой задачи была бы реализация на двумерном инт-масиве с размерами как у полей в вашей игре (самый простой вариант 3х3, например).
то-есть при старте игры мы устанавливаем все значения в масиве в какое-нибудь число, например 5 (это будет значить что никто еще не делал ход и 5 — значит пустая клетка)

|5|5|5|
|5|5|5|
|5|5|5|

потом предположим компьютер делает первый ход — обозначем его цифрой 1 (это как крестик).

|5|5|5|
|5|1|5|
|5|5|5|

игрок ему отвечает и ставит где-то 0 (нужно проверить сначала что в этой клетке стоит 5 (то-есть она пуста) ):

|5|5|5|
|5|1|5|
|5|5|0|

и так далее..
после каждого хода мы запускаем функцию, которая проверяет не победил ли походивший игрок (нет ли по вертикали, горизонтали, диагонали ряда 1 или 0).
и задача отрисовки будет просто сводиться к кейс-свичу вида:

если 0: нарисовать 0, 
если 1: нарисовать +, 
5: ниче не рисовать

Такое отделение логики игры от ее отрисовки сделает написание игры в разы проще и понятнее, и запутаться здесь практически негде.

Понравилась статья? Поделить с друзьями:
  • Криптопро office signature скачать бесплатно для windows 10
  • Крестики на иконках windows 10 как убрать
  • Криптопро csp скачать бесплатно для windows 7 с лицензией
  • Крестик на иконке звука windows 10
  • Криптопро csp скачать бесплатно для windows 10 через торрент