Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions TicTacToe/TicTacToe.Tests/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// <copyright file="GlobalSuppressions.cs" company="Ilya Krivtsov">
// Copyright (c) Ilya Krivtsov. All rights reserved.
// </copyright>

// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
using System.Diagnostics.CodeAnalysis;

[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "This is project with tests")]
27 changes: 27 additions & 0 deletions TicTacToe/TicTacToe.Tests/TicTacToe.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageReference Include="NUnit" Version="4.2.2" />
<PackageReference Include="NUnit.Analyzers" Version="4.4.0" />
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
</ItemGroup>

<ItemGroup>
<Using Include="NUnit.Framework" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="../TicTacToe/TicTacToe.csproj" />
</ItemGroup>

</Project>
66 changes: 66 additions & 0 deletions TicTacToe/TicTacToe.Tests/TicTacToeGameTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// <copyright file="TicTacToeGameTests.cs" company="Ilya Krivtsov">
// Copyright (c) Ilya Krivtsov. All rights reserved.
// </copyright>

namespace TicTacToe.Tests;

public class TicTacToeGameTests
{
private TicTacToeGame game;

[SetUp]
public void Setup()
{
game = new();
}

[Test]
public void FirstRow_OfX_Wins()
{
Assert.That(game.MakeMove(0, 0), Is.EqualTo(MoveResult.XTurn));
Assert.That(game.MakeMove(0, 1), Is.EqualTo(MoveResult.OTurn));

Assert.That(game.MakeMove(1, 0), Is.EqualTo(MoveResult.XTurn));
Assert.That(game.MakeMove(1, 1), Is.EqualTo(MoveResult.OTurn));

Assert.That(game.MakeMove(2, 0), Is.EqualTo(MoveResult.XWins));
}

[Test]
public void FirstRow_OfO_Wins()
{
Assert.That(game.MakeMove(0, 0), Is.EqualTo(MoveResult.XTurn));
Assert.That(game.MakeMove(0, 1), Is.EqualTo(MoveResult.OTurn));

Assert.That(game.MakeMove(1, 0), Is.EqualTo(MoveResult.XTurn));
Assert.That(game.MakeMove(1, 1), Is.EqualTo(MoveResult.OTurn));

Assert.That(game.MakeMove(2, 2), Is.EqualTo(MoveResult.XTurn));
Assert.That(game.MakeMove(2, 1), Is.EqualTo(MoveResult.OWins));
}

[Test]
public void FirstColumn_OfO_Wins()
{
Assert.That(game.MakeMove(1, 0), Is.EqualTo(MoveResult.XTurn));
Assert.That(game.MakeMove(0, 0), Is.EqualTo(MoveResult.OTurn));

Assert.That(game.MakeMove(1, 2), Is.EqualTo(MoveResult.XTurn));
Assert.That(game.MakeMove(0, 1), Is.EqualTo(MoveResult.OTurn));

Assert.That(game.MakeMove(2, 2), Is.EqualTo(MoveResult.XTurn));
Assert.That(game.MakeMove(0, 2), Is.EqualTo(MoveResult.OWins));
}

[Test]
public void Diagonal_OfX_Wins()
{
Assert.That(game.MakeMove(0, 0), Is.EqualTo(MoveResult.XTurn));
Assert.That(game.MakeMove(0, 1), Is.EqualTo(MoveResult.OTurn));

Assert.That(game.MakeMove(1, 1), Is.EqualTo(MoveResult.XTurn));
Assert.That(game.MakeMove(1, 2), Is.EqualTo(MoveResult.OTurn));

Assert.That(game.MakeMove(2, 2), Is.EqualTo(MoveResult.XWins));
}
}
10 changes: 10 additions & 0 deletions TicTacToe/TicTacToe.UI/App.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="TicTacToe.UI.App"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->

<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>
27 changes: 27 additions & 0 deletions TicTacToe/TicTacToe.UI/App.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// <copyright file="App.axaml.cs" company="Ilya Krivtsov">
// Copyright (c) Ilya Krivtsov. All rights reserved.
// </copyright>

namespace TicTacToe.UI;

using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;

public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}

public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow();
}

base.OnFrameworkInitializationCompleted();
}
}
16 changes: 16 additions & 0 deletions TicTacToe/TicTacToe.UI/GlobalSuppressions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// <copyright file="GlobalSuppressions.cs" company="Ilya Krivtsov">
// Copyright (c) Ilya Krivtsov. All rights reserved.
// </copyright>

// This file is used by Code Analysis to maintain SuppressMessage
// attributes that are applied to this project.
// Project-level suppressions either have no target or are given
// a specific target and scoped to a namespace, type, member, etc.
using System.Diagnostics.CodeAnalysis;

[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Entry point", Scope = "type", Target = "~T:TicTacToe.UI.Program")]

[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1601:Partial elements should be documented", Justification = "Application class", Scope = "type", Target = "~T:TicTacToe.UI.App")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1601:Partial elements should be documented", Justification = "Window class", Scope = "type", Target = "~T:TicTacToe.UI.MainWindow")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Application class", Scope = "type", Target = "~T:TicTacToe.UI.App")]
[assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1600:Elements should be documented", Justification = "Window class", Scope = "type", Target = "~T:TicTacToe.UI.MainWindow")]
25 changes: 25 additions & 0 deletions TicTacToe/TicTacToe.UI/MainWindow.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Window xmlns="https://github.com/avaloniaui"
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"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="TicTacToe.UI.MainWindow"
Title="TicTacToe.UI"
CanResize="false"
Width="640"
Height="640"
Background="#eeeeee">

<Grid x:Name="mainGrid" ShowGridLines="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
</Grid>
</Window>
79 changes: 79 additions & 0 deletions TicTacToe/TicTacToe.UI/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// <copyright file="MainWindow.axaml.cs" company="Ilya Krivtsov">
// Copyright (c) Ilya Krivtsov. All rights reserved.
// </copyright>

namespace TicTacToe.UI;

using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.VisualTree;
using MsBox.Avalonia;

public partial class MainWindow : Window
{
private readonly TicTacToeGame game;

private bool won = false;

public MainWindow()
{
InitializeComponent();

game = new();

for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
var button = new Button()
{
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Stretch,
VerticalAlignment = Avalonia.Layout.VerticalAlignment.Stretch,
HorizontalContentAlignment = Avalonia.Layout.HorizontalAlignment.Center,
VerticalContentAlignment = Avalonia.Layout.VerticalAlignment.Center,
FontSize = 64,
};
int row = i;
int column = j;

button.Click += (o, e) => OnClick(button, row, column);

Grid.SetRow(button, row);
Grid.SetColumn(button, column);
mainGrid.Children.Add(button);
}
}
}

private void OnClick(Button button, int row, int column)
{
if (won)
{
return;
}

var result = game.MakeMove(column, row);

var xTurn = result is MoveResult.XTurn or MoveResult.XWins;
var oTurn = result is MoveResult.OTurn or MoveResult.OWins;

if (!xTurn && !oTurn)
{
return;
}

if (result is MoveResult.XWins or MoveResult.OWins)
{
won = true;
MessageBoxManager
.GetMessageBoxStandard("Win!", $"{(result == MoveResult.XWins ? "X" : "O")} has won!")
.ShowWindowAsync();
}

button.Content = xTurn ? "X" : "O";
button.Foreground = xTurn ? SolidColorBrush.Parse("#f02020") : SolidColorBrush.Parse("#2020f0");
}
}
25 changes: 25 additions & 0 deletions TicTacToe/TicTacToe.UI/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// <copyright file="Program.cs" company="Ilya Krivtsov">
// Copyright (c) Ilya Krivtsov. All rights reserved.
// </copyright>

namespace TicTacToe.UI;

using System;
using Avalonia;

public class Program
{
// Initialization code. Don't use any Avalonia, third-party APIs or any
// SynchronizationContext-reliant code before AppMain is called: things aren't initialized
// yet and stuff might break.
[STAThread]
public static void Main(string[] args) => BuildAvaloniaApp()
.StartWithClassicDesktopLifetime(args);

// Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp()
=> AppBuilder.Configure<App>()
.UsePlatformDetect()
.WithInterFont()
.LogToTrace();
}
22 changes: 22 additions & 0 deletions TicTacToe/TicTacToe.UI/TicTacToe.UI.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<BuiltInComInteropSupport>true</BuiltInComInteropSupport>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Avalonia" Version="11.2.7" />
<PackageReference Include="Avalonia.Desktop" Version="11.2.7" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.2.7" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.7" />
<PackageReference Include="MessageBox.Avalonia" Version="3.2.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="../TicTacToe/TicTacToe.csproj" />
</ItemGroup>
</Project>
18 changes: 18 additions & 0 deletions TicTacToe/TicTacToe.UI/app.manifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<!-- This manifest is used on Windows only.
Don't remove it as it might cause problems with window transparency and embedded controls.
For more details visit https://learn.microsoft.com/en-us/windows/win32/sbscs/application-manifests -->
<assemblyIdentity version="1.0.0.0" name="TicTacToe.UI.Desktop"/>

<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->

<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
</assembly>
34 changes: 34 additions & 0 deletions TicTacToe/TicTacToe.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TicTacToe", "TicTacToe\TicTacToe.csproj", "{13DED376-EA25-47D0-A76A-1C417967F3E3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TicTacToe.Tests", "TicTacToe.Tests\TicTacToe.Tests.csproj", "{671F028F-5CA0-4D46-94D7-5250C1EBEAEF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TicTacToe.UI", "TicTacToe.UI\TicTacToe.UI.csproj", "{D1BAFEA2-7EEA-4602-98A0-F639C9265A2D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{13DED376-EA25-47D0-A76A-1C417967F3E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{13DED376-EA25-47D0-A76A-1C417967F3E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{13DED376-EA25-47D0-A76A-1C417967F3E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{13DED376-EA25-47D0-A76A-1C417967F3E3}.Release|Any CPU.Build.0 = Release|Any CPU
{671F028F-5CA0-4D46-94D7-5250C1EBEAEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{671F028F-5CA0-4D46-94D7-5250C1EBEAEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{671F028F-5CA0-4D46-94D7-5250C1EBEAEF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{671F028F-5CA0-4D46-94D7-5250C1EBEAEF}.Release|Any CPU.Build.0 = Release|Any CPU
{D1BAFEA2-7EEA-4602-98A0-F639C9265A2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D1BAFEA2-7EEA-4602-98A0-F639C9265A2D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D1BAFEA2-7EEA-4602-98A0-F639C9265A2D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D1BAFEA2-7EEA-4602-98A0-F639C9265A2D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
26 changes: 26 additions & 0 deletions TicTacToe/TicTacToe/Cell.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// <copyright file="Cell.cs" company="Ilya Krivtsov">
// Copyright (c) Ilya Krivtsov. All rights reserved.
// </copyright>

namespace TicTacToe;

/// <summary>
/// Cell on the tic-tac-toe board.
/// </summary>
public enum Cell
{
/// <summary>
/// Empty cell.
/// </summary>
Empty,

/// <summary>
/// X cell.
/// </summary>
X,

/// <summary>
/// O cell.
/// </summary>
O,
}
Loading
Loading