УДК 004
Федотов В.А. студент 2 курса
факультет «Информационные системы и технологии» Северный Арктический федеральный университет Высшая школа информационных технологий и
автоматизированных систем Россия, г. Архангельск РАЗРАБОТКА ИГРОВОГО ПРИЛОЖЕНИЯ КРЕСТИКИ - НОЛИКИ НА ЯЗЫКЕ ПРОГРАММИРОВАНИЯ С# Аннотация: Статья посвящена разработке игрового приложения крестики - нолики на языке программирования с#.
Ключевые слова: Приложение, крестики-нолики, с#, программирование.
Fedotov V.A.
student, 2year, faculty "Information Systems and Technology" Northern Arctic Federal University, Graduate School of Information
Technology and Automated Systems Russia, Arkhangelsk DEVELOPMENT OF THE GAME APPLICATION TIC-TAC-TOE IN C # PROGRAMMING LANGUAGE Annotation: The article is devoted to the development of the game application tic-tac-toe in the programming language C #.
Keywords: Application, tic-tac-toe, c #, programming.
1 РЕАЛИЗАЦИЯ
Язык C# является наиболее известной новинкой в области создания языков программирования. По сути, это язык программирования, созданный уже в 21-м веке. Явившись на свет в недрах Microsoft, он с первых своих шагов получил мощную поддержку. Язык признан международным сообществом.
Первым делом для реализации игры крестики-нолики нужно создать игровое поле размером 3*3, используя массив массивов, в соответствии с
рисунком 1._
private int n = 3; //Размер игрового поля public Cell[][] Map { get; } //Обозначение массива
public TicTacToeModel() //Создается массив массивов {
Map = Enumerable.Range(0, n) //Создание входной последовательности
.Select(i => Enumerable.Range(0, n) .Select(j => new Cell()) .ToArrayO) .ToArray();
}_
Рисунок 1 - Код для создания поля массива
Далее создаем новый класс Cell, в котором создаем метод State для получения одного из состояний ячейки, в соответствии с рисунком 2._
public class Cell : INotifyPropertyChanged {
public State State {get; private set;} = State.None; }_
Рисунок 2 - Состояние ячейки
Затем создаем новый метод enum для получения одного из трех (None,
X, O) состояний ячейки, в соответствии с рисунком 3._
public enum State {None, X, O}
Рисунок 3 - Получение информации о трех возможных состояниях клетки
Потом в Model.cs создаем метод, который позволяет сделать ход пользователю, в соответствии с рисунком 4._
public WinPlayer Switch(Cell cell) //Ход Х {
var state = step++ % 2 == t ? State.X : State.O; cell.SwitchTo(state); return WinTest() ? state == State.X ? WinPlayer.Player_X : WinPlayer.Player_O : step == 9 ? WinPlayer.Standoff : WinPlayer.Unknown; Рисунок 4 - Ход пользователя
Далее также в Model.cs создаем новый метод, который делает ход за компьютер, в соответствии с рисунком 5._
public WinPlayer RandomStep() //Рандомный ход компьютера 0 {
var k = rnd.Next(9 - step)+1; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) if (Map[i][j].State == State.None && --k == 0) return Switch(Map[i][j]);
return WinPlayer.Standoff;
}_
Рисунок 5 - Ход компьютера
Дальше создаем новый метод WinTest(), который проверяет победную
стратегию крестика или нолика, в соответствии с рисунком 6._
public bool WinTest() //Проверка победы крестика или нолика
{
bool[] rows = { true, true, true }; for (int i = 0; i < n; i++) for (int j = 1; j < n; j++) rows[i] &= Map[i][j].State != State.None && Map[i][j].State == Map[i][0].State;
bool[] cols = { true, true, true }; for (int j = 0; j < n; j++) for (int i = 1; i < n; i++) cols[j] &= Map[i][j].State != State.None && Map[i][j].State == Map[0][j].State;
bool mainD = Map[0][0].State != State.None && Map[0][0].State == Map[1][1].State && Map[0][0].State == Map[2][2].State;
bool collateralD = Map[2][0].State != State.None && Map[2][0].State == Map[1][1].State && Map[2][0].State == Map[0][2].State;
return rows.Any(x=>x) || cols.Any(x => x) || mainD || collateralD;
}_
Рисунок 6 - Проверка победной стратегии
Далее создаем новый метод X_or_0, который дает возможность
пользователю выбирать за кого играть, в соответствии с рисунком 7._
public void X_or_0(int newT=> t = newT; //Переключение между X и 0 Рисунок 7 - Выбор крестика или нолика
Далее создаем новый метод Message для вывода сообщения о победе крестика или нолика, или ничьей, в соответствии с рисунком 8._
public string Message //Вывод сообщения о победе {
get => message;
set
{
message = value;
MessageVisibility = (value == "") ? Visibility.Collapsed : Visibility.Visible;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs (nameof(MessageVisibility)));
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs (nameof(Message)));
}
}
public Visibility MessageVisibility { get; private set; } =
Visibility.Collapsed; Рисунок 8 - Сообщение о победе или ничьей
Для перезапуска игры создаем новый метод NewGame(), в соответствии с рисунком 9._
public void NewGame() //Новая игра {
for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) Map[i][j].SwitchTo(State.None); step = 0; Message = "";
}_
Рисунок 9 - Метод для новой игры
Для подсчета времени, сколько длится игра, можно установить таймер, который будет останавливаться после выигрыша крестика или нолика и обнуляться после каждого перезапуска игры. Для этого в Xaml.cs создадим новый метод Dt Tick, в соответствии с рисунком 10._
private void Dt_Tick(object sender, EventArgs e) // Таймер {
tblTimer.Text = (DateTime.Now -
startTime).TotalSeconds.ToString("F1");
}_
Рисунок 10 - Таймер 2 ИНТЕРФЕЙС
При запуске данного проекта «Крестики - нолики» запускается стартовое окно, которое имеет вид, в соответствии с рисунком 11.
Рисунок 11 - Стартовое окно
Настройки окна имеют вид, в соответствии с рисунком 12._
TexЮptюш.TextFormattmgMode-,Display" MinWidth="450" MinHeight="450" Width="400" Height="400" ^Мй^'^ЛЬ" ShowInTaskbar="True" Margin="2" Foreground="{x:Null}"> <Window.Background> <ImageBrush ImageSource="Image/FON.jpg"/> </Window.Background> <Grid>
<Button x:Name="button1" Width="250" Height="50" Content="New
Game"
Qick="button1_Qick" //Кнопка «NewGame» на стартовом окне HorizontalAlignment="Center" //Выравнивание по горизонтали VerticalAlignment="Center" //Выравнивание по вертикали FontSize="36" //Размер шрифта 0pacity="0.8" //Прозрачность Foreground="MidnightBlue" //Цвет текста Background="MintCream" //Цвет кнопки BorderBrush="{x:Null}" //Отсутствие рамки кнопки FontWeight="Bold" //Начертание текста - полужирный FontFamily="Peace Sans"> //Шрифт текста </Button> </Grid>
Рисунок 12 - Настройки окна
При нажатии на кнопку «NewGame» выполняется переход уже на игровое окно, в котором находится игровое поле, а также кнопки выбора игры за X или 0, кнопки рестарт, выхода и вернуться назад на стартовое окно.
Игровое окно имеет вид, в соответствии с рисунком 13.
* "Пс-Тас-Тае — □ X
Рисунок 13 - Игровое окно
_Оформление окна имеет код, в соответствии с рисунком 14._
TexЮptюш.TextFormattmgMode-,Display" MmWidth="450" MinHeight="450" Width="500" Height="550" ^Мй^'^ЛЬ" ShowInTaskbar="True" Margin="2"> <Window.Background> <ImageBrush ImageSource="Image/FON12.jpg"/> </Window.Background> Рисунок 14 - Оформление окна
При нажатии на игровое поле, появляется крестик или нолик, в соответствии с тем, кого выбрал игрок, как изображено на рисунке 15.
I "Пс-Тас-Тое -ОХ
Рисунок 15 - Изображения крестика и нолика
Сами изображения крестика или нолика реализуются, в соответствии с
рисунком 16 и рисунком 17._
<DataTemplate x:Key-,dtState.X">
<Image Source="Image/Cross.png" Opacity="1"> <Image.Triggers> <EventTrigger RoutedEvent-,Image.Loaded"> <BeginStoryboard> <Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="1"
^="1" Duration="0:0:0.2"/> </Storyboard> </BeginStoryboard> </EventTrigger> </Image.Triggers> </Image> </Grid> </DataTemplate>
Рисунок 16 - Реализация крестика
<DataTemplate x:Key-,dtState.O"> <Grid>
<Image Source="Image/Cross1.png" Opacity="1"> <Image.Triggers> <EventTrigger RoutedEvent="Image.Loaded"> <BeginStoryboard> <Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity"
From="1"
^="1" Duration="0:0:0.2" BeginTime="0:0:0.2"/> </Storyboard> </BeginStoryboard> </EventTrigger> </Image.Triggers> </Image> </Grid> </DataTemplate> Рисунок 17 - Реализация нолика
Кнопка рестарт, которая перезапускает игру, используя метод
NewGame(), имеет код, изображенный на рисунке 18._
^ШЬП x:Name="button1" Width="120" Height="40" Content="Restart" aick="button1_aick" Margin="10,10,365,470" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="28" Foreground="White" BorderBrush="{x:Null}" Background="{x:Null}" FontWeight="Bold" FontFamily="Peace Sans">
</Button>_
Рисунок 18 - Кнопка рестарт
Кнопка Р1ауегХ дает возможность выбора играть за крестик. Кнопка
имеет код, изображенный на рисунке 19._
^ШЬП x:Name="button3" Width="110" Height="40" Content="Player X" aick="button3_aick" Margin="-40,45,330,435" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="22" Foreground="White" BorderBrush="{x:Null}" Background="{x:Null}" FontWeight="Bold" FontFamily="Peace Sans">
</Button>_
Рисунок 19 - Кнопка PlayerX
Кнопка Р1ауег0 дает возможность выбора играть за нолик. Кнопка имеет
код, изображенный на рисунке 20._
^ШЬП x:Name="button4" Width="110" Height="40" Content="Player 0" Qick="button4_Qick" Margin="60,45,190,435" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="22" Foreground="White" BorderBrush="{x:Null}" Background="{x:Null}" FontWeight="Bold" FontFamily="Peace Sans">
</Button>_
Рисунок 20 - Кнопка Player0
_Кнопка Exit закрывает приложение. Код изображен на рисунке 21._
<Button x:Name-"button2" Width="120" Height-40" Content-'Exit" Click="button2_Click" Margin="340,460,40,20" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="28" Foreground="White" BorderBrush="{x:Null}" Background="{x:Null}" FontWeight="Bold" FontFamily-"Peace Sans">
</Button>_
Рисунок 21 - Кнопка Exit
Для связывания Cell[][] Map (Создание поля) из model.cs с MainWindow.xaml в MainWindow.xaml используется код, изображенный на
рисунке 22._
<ItemsControl ItemsSource="{Binding Map}" HorizontalAlignment-'Center" VerticalAlignment="Center"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate> <DataTemplate> <ItemsControl ItemsSource="{Binding}"> <ItemsControl.ItemTemplate> <DataTemplate> <Border Background="#FAFAD2" MouseDown="Cell_MouseDown" CornerRadius="20" 0pacity="0.8" Width="100" Height="100" Margin-'1" Padding="15"> <ContentControl Content-"{Binding State}" ContentTemplateSelector-"{StaticResource dtCell}"/> </Border> </DataTemplate>
</ItemsControl.ItemTemplate> </ItemsControl> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> Рисунок 22 - Отображение игрового поля
Далее для отображения надписи о выигрыше крестика или нолика, или
ничьей для связи используется код, изображенный на рисунке 23._
<Border Visibility-'{Binding MessageVisibility}" HorizontalAlignment="Center" VerticalAlignment="Center" MouseDown="Message_MouseDown" > <Border.Background> <ImageBrush/> </Border.Background> <TextBlock Text="{Binding Message}" Width="500" Height="320" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FontSize="56" Foreground="MintCream" FontWeight="Bold"> <TextBlock.BitmapEffect> <DropShadowBitmapEffect/> </TextBlock.BitmapEffect> </TextBlock> </Border>
Рисунок 23 - Отображение надписи выигрыша/ничьей
Для визуального отображения таймера используем код, изображенный
на рисунке 24._
<TextBlock Name="tblTimer" HorizontalAlignment="Right" VerticalAlignment="Top" FontSize="20" Width="30" Height="30"
Margin="10"/>_
Рисунок 24 - Таймер 3 тестирование
Тестирование происходило в два этапа. Для начала проверялся разработанный функционал игры, соответственно на работоспособность кода, кнопок, алгоритма программы. Во втором этапе тестирования проверялся
интерфейс, который должен быть доступен и понятен каждому.
Таким образом, в процессе тестирования, сопровождающимся процессом разработки, на начальном этапе разработки удалось устранить недостатки и усовершенствовать приложение.
заключение
Программа предоставляет возможность играть с компьютером, который действует согласно созданному алгоритму. В приложении учтены моменты, позволяющие пользователю легко освоить программу, для этого необходимо создать удобный и простой интерфейс, который является «визитной карточкой» приложения. В приложение можно внести некоторые доработки, например, добавить «интеллект» для хода компьютера.
Использованные источники:
1. Барков, И.А. Объектно-ориентированное программирование : учебник / И.А. Барков. — Санкт-Петербург : Лань, 2019. — 700 с. — ISBN 978-5-81143586-9. — Текст : электронный // Электронно-библиотечная система «Лань» : [сайт]. — URL: https://elanbook.com/book/119661 (дата обращения: 28.08.2019). — Режим доступа: для авториз. пользователей.
2. Залогова, Л.А. Основы объектно-ориентированного программирования на базе языка С# : учебное пособие / Л.А. Залогова. — Санкт-Петербург : Лань, 2018. — 192 с. — ISBN 978-5-8114-3093-2. — Текст : электронный // Электронно-библиотечная система «Лань» : [сайт]. — URL: https://e.lanbook.com/book/106731 (дата обращения: 28.08.2019). — Режим доступа: для авториз. пользователей.
Приложение А (Обязательное) Исходный код Windowl.xaml <Window x:Class="TicTacToe.Window1" xmlns-"http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microso^.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns: mc="http: //schemas .openxmlformats. org/markup-compatibility/2006"
xmlns: local-'clr-namespace: TicTacToe"
mc:Ignorable="d"
Title-'Windowl"
TextOptions.TextFormattingMode="Display" MinWidth-"450" MinHeight-"450" Width-"450" Height-"400" Visibility-"Visible" ShowInTaskbar-"True" Margin-"2" Foreground-"{x:Null}"> <Window.Background> <ImageBrush ImageSource-"Image/FON.jpg"/> </Window.Background>
<Grid>
<Button x:Name-"button1" Width-"250" Height-"50" Content-"New Game" Click-"button1_Click"
HorizontalAlignment-"Center" VerticalAlignment-"Center" FontSize-"36" 0pacity-"0.8"
Foreground-"MidnightBlue" Background-"MintCream" BorderBrush-"{x:Null}" FontWeight-"Bold" FontFamily-"Peace Sans"> </Button> </Grid> </Window>
Приложение Б (Обязательное) Исходный код Window1.xaml.cs using System.Windows; namespace TicTacTo { public partial class Window1 : Window { public Window1(){ InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e){ MainWindow taskWindow - new MainWindow(); taskWindow.Show();
Close(); }
}
}
Приложение В (Обязательное) Исходный код MainWindow.xaml <Window x:Qass=,TicTacToe.MainWindow" xmlns=мhttp://schemas.microsoft.com/winfx/2006/xaml/presentationм xmlns:x=мhttp://schemas.microsoft.com/winfX/2006/xaml" xmlns:d=мhttp://schemas.microsoft.com/expression/blend/2008м xmlns: mc=мhttp: //schemas .openxmlformats. org/markup-compatibility/2006м
xmlns: local="clr-namespace: TicTacToe"
mc:Ignorable=мdм
Title="Tic-Tac-Toe"
TextOptions.TextFormattingMode=мDisplayм MinWidth=м450м MinHeight=м450м Width=м500м Height="550" Visibility="Visible" ShowInTaskbar=мTrueм Margin=м2м> <Window.Background>
<ImageBrush ImageSource=мImage/FON12.jpgм/> </Window.Background>
<Window.Resources> <local:CellDataTemplateSelector x:Key=мdtCellм/> <DataTemplate x:Key=мdtState.Xм> <Grid>
<Image Source="Image/Cross.png" Opacity=м1м> <Image.Triggers> <EventTrigger RoutedEvent=мImage.Loadedм> <BeginStoryboard> <Storyboard>
<DoubleAnimation Storyboard.TargetProperty=мOpacityм From="0" ^="1" Duration=м0:0:0.2м/> </Storyboard> </BeginStoryboard> </EventTrigger> </Image.Triggers>
</Image> </Grid> </DataTemplate>
<DataTemplate x:Key="dtState.O"> <Grid>
<Image Source=мImage/Тaught.pngм Opacity=м1м> <Image.Triggers> <EventTrigger RoutedEvent="Image.Loaded"> <BeginStoryboard>
<Storyboard>
<DoubleAnimation Storyboard.TargetProperty="Opacity" From="0" То="1" Duration="0:0:0.2" BeginTime="0:0:0.2"/> </Storyboard> </BeginStoryboard> </EventTrigger> </Image.Triggers> </Image> </Grid> </DataTemplate> <DataTemplate x:Key="dtState.None"/> </Window.Resources> <Grid>
<Button x:Name="button1" Width="120" Height="40" Content="Restart" aick="button1_aick" Margin="10,10,365,470" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="28" Foreground="White" BorderBrush="{x:Null}" Background="{x:Null}" FontWeight="Bold" FontFamily="Peace Sans"> </Button>
<Ви«оп x:Name="button2" Width="120" Height="40" Content="Exit" СНск="Ьи«оп2_СНск"
Margin="340,460,40,20" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="28" Foreground="White" BorderBrush="{x:Null}" Background="{x:Null}" FontWeight="Bold" FontFamily="Peace Sans"> </Button>
<Button x:Name="button3" Width="110" Height="40" Content="Player X" Click="button3_aick"
Margin="-40,45,330,435" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="22" Foreground="White"
BorderBmsh="{x:Null}" Background="{x:Null}" FontWeight="Bold" FontFamily="Peace Sans"> </Button>
<Button x:Name="button4" Width="110" Height="40" Content="Player 0" Click="button4_Click"
Margin="60,45,190,435" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="22" Foreground="White" BorderBrush="{x:Null}" Background="{x:Null}" FontWeight="Bold" FontFamily="Peace Sans"> </Button>
<Button x:Name="button5" Width="100" Height="40" Content="Back" Click="button5_Click"
Margin="17,460,376.6,20" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="28" Foreground="White" BorderBrush=" {x:Null}" Background="{x:Null}" FontWeight="Bold" FontFamily="Peace Sans"> </Button>
<ItemsControl ItemsSource="{Binding Map}" HorizontalAlignment="Center" VerticalAlignment="Center"> <ItemsControl.ItemsPanel>
<ItemsPanelTemplate> <StackPanel Orientation="Horizontal"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemTemplate> <DataTemplate> <ItemsControl ItemsSource=" {Binding} "> <ItemsControl.ItemTemplate> <DataTemplate> <Border Background="#FAFAD2" MouseDown="Cell MouseDown"
CornerRadius="20" 0pacity="0.8" Width="100" Height="100" Margin="1" Padding="15"> <ContentControl Content="{Binding State}" ContentTemplateSelector="{StaticResource dtCell}"/> </Border> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl>
<Border Visibility="{Binding MessageVisibility}" HorizontalAlignment="Center" VerticalAlignment="Center" MouseDown="Message_MouseDown" > <Border.Background> <ImageBrush/> </Border.Background> <TextBlock Text="{Binding Message}" Width="500" Height="320" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" FontSize="56" Foreground="MintCream" FontWeight="Bold"> <TextBlock.BitmapEffect> <DropShadowBitmapEffect/> </TextBlock.BitmapEffect> </TextBlock> </Border>
<TextBlock Name="tblTimer" HorizontalAlignment="Right" VerticalAlignment="Top"
FontSize="20" Width="30" Height="30" Мш^т="107> </Grid> </Window>
Приложение Г (Обязательное) Исходный код MainWindow.xaml.cs
using System;
using System.ComponentModel; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Threading;
namespace TicTacToe {
/// <summary>
/// Логика взаимодействия для MamWmdow.xaml /// </summary> public partial class MainWindow : Window { DispatcherTimer dt = new DispatcherTimer(); DateTime startTime;
private TicTacToeModel model = new TicTacToeModel(); public MainWindow(){ InitializeComponent(); DataContext = model; dt.Tick += Dt_Tick;
dt.Interval = TimeSpan.FromMilliseconds(25);
startTime = DateTime.Now;
dt.Start();
}
private void Cell_MouseDown(object sender, MouseButtonEventArgs e) //Вывод сообщения о победе {
var cell = (sender as Border).DataContext as Cell; if (cell.State == State.None){ var winTest = model.Switch(cell); if (winTest == WinPlayer.Unknown) winTest = model.RandomStep(); if (winTest != WinPlayer.Unknown && winTest != WinPlayer.Standoff){
model.Message = winTest + " победил "; dt.Stop();
}
else if (winTest == WinPlayer.Standoff)
{
model.Message = "Ничья"; dt.Stop();
}
}
}
private void Dt_Tick(object sender, EventArgs e) // Таймер {
tblTimer.Text - (DateTime.Now -
startTime).TotalSeconds.ToString("F1");
}
private void Message_MouseDown(object sender,
MouseButtonEventArgs e) //При клике в область Message начало новой игры {
model.NewGame();
}
private void button1_Click(object sender, RoutedEventArgs e)
//Кнопка начала новой игры {
model.NewGame(); startTime - DateTime.Now; dt.Start();
}
private void button2_Click(object sender, RoutedEventArgs e) //Кнопка выхода
{
Close();
}
private void button3_Click(object sender, RoutedEventArgs e) //Игра за
крестик {
model.NewGame(); model.X_or_0(0);
}
private void button4_Click(object sender, RoutedEventArgs e) //Игра за
нолик
{
model.NewGame(); model.X_or_0( 1);
}
private void button5_Click(object sender, RoutedEventArgs e) //Кнопка
выхода из игры {
Window1 taskWindow - new Window1(); taskWindow.Show();
Close(); }
}
public class Cell : INotifyPropertyChanged {
public State State { get; private set; } = State.None;
public void SwitchTo(State state) {
State = state;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("State"));
// PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs("Uru")); }
public event PropertyChangedEventHandler PropertyChanged; }
public enum State { None, X, O }
public class CellDataTemplateSelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item,
DependencyObject container)
{
DataTemplate resource(string s) => (container as FrameworkElement).FindResource(s) as DataTemplate;
if (item != null && item is State)
{
var state = (State)item; if (state == State.None) return resource("dtState.None"); else if (state == State.X) return resource("dtState.X");
else if (state == State.O) return resource("dtState.O"); }
return null;
}
} }
Приложение Д (Обязательное) Исходный код TicTacToeModel.cs
using System;
using System.ComponentModel; using System.Linq; using System.Windows;
namespace TicTacToe {
internal class TicTacToeModel : INotifyPropertyChanged {
private int step - 0, t - 0;
private int n - 3;
private string message;
private Random rnd - new Random();
public event PropertyChangedEventHandler PropertyChanged;
public Cell[][] Map { get; } //Создание поля для игры
public string Message //Вывод сообщения о победе {
get -> message;
set {
message - value;
MessageVisibility - (value -- "") ? Visibility.Collapsed : Visibility.Visible;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(MessageVisibility))); PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(nameof(Message))); }
}
public Visibility MessageVisibility { get; private set; } -Visibility.Collapsed;
public TicTacToeModel() //Создается массив массивов {
Map - Enumerable.Range(0, n) .Select(i -> Enumerable.Range(0, n) .Select(j -> new Cell()) .ToArray()) .ToArray();
}
public void NewGame() //Новая игра {
for (int i - 0; i < n; i++) for (int j - 0; j < n; j++)
Map[i][j].SwitchTo(State.None); step = 0; Message = "";
}
public WinPlayer Switch(Cell cell) //Ход Х {
var state = step++ % 2 == t ? State.X : State.O; cell.SwitchTo(state); return WinTest()
? state == State.X ? WinPlayer.Player_X : WinPlayer.Player_O : step == 9 ? WinPlayer.Standoff : WinPlayer.Unknown;
}
public WinPlayer RandomStep() //Рандомный ход компьютера 0 {
var k = rnd.Next(9 - step)+1; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) if (Map[i][j].State == State.None && --k == 0) return Switch(Map[i][j]); return WinPlayer.Standoff;
}
public bool WinTest() //Проверка победы крестика или нолика {
bool[] rows = { true, true, true }; for (int i = 0; i < n; i++) for (int j = 1; j < n; j++)
rows[i] &= Map[i][j].State != State.None && Map[i][j].State ==
Map[i][0].State;
bool[] cols = { true, true, true }; for (int j = 0; j < n; j++) for (int i = 1 ; i < n; i++) cols[j] &= Map[i][j].State != State.None && Map[i][j].State == Map[0][j].State;
bool mainD = Map[0][0].State != State.None && Map[0][0].State == Map[1][1].State && Map[0][0].State == Map[2][2].State;
bool collateralD = Map[2][0].State != State.None && Map[2][0].State == Map[1][1].State && Map[2][0].State == Map[0][2].State;
return rows.Any(x=>x) || cols.Any(x => x) || mainD || collateralD;
}
public void X_or_0(int newT) => t = newT; //Переключение между X и 0 enum WinPlayer { Unknown, Player_X, Player_O, Standoff } //Перечисление