lr5 Ukr

You might also like

You are on page 1of 23

Міністерство освіти і науки України

Національний технічний університет України «Київський політехнічний


інститут імені Ігоря Сікорського"
Факультет інформатики та обчислювальної техніки

Кафедра інформатики та програмної інженерії

Звіт

з лабораторної роботи № 5 з дисципліни


«Проектування алгоритмів»

„Пошук в умовах протидії, ігри з повною інформацією”

Виконав(ла) Гавриленко Ян Сергійович


(шифр, прізвище, ім'я, по батькові)

Перевірив Головченко М.Н.


(прізвище, ім'я, по батькові)

Київ 2021
ЗМІСТ

1 МЕТА ЛАБОРАТОРНОЇ РОБОТИ......................................................3

2 ЗАВДАННЯ...............................................................................................4

3 ВИКОНАННЯ...........................................................................................6

3.1 ПРОГРАМНА РЕАЛІЗАЦІЯ АЛГОРИТМУ....................................................6


3.1.1 Вихідний код.....................................................................................6
3.1.2 Приклади роботи............................................................................6
3.3 ТЕСТУВАННЯ АЛГОРИТМУ......................................................................6

ВИСНОВОК.....................................................................................................7

КРИТЕРІЇ ОЦІНЮВАННЯ..........................................................................8

2
1 МЕТА ЛАБОРАТОРНОЇ РОБОТИ
Мета роботи - вивчити основні підходи до формалізації алгоритмів
знаходження рішень задач в умовах протидії. Ознайомитися з підходами до
програмування алгоритмів штучного інтелекту в іграх з повною інформацією.

3
2 ЗАВДАННЯ

Згідно варіанту (таблиця 2.1) реалізувати візуальний ігровий додаток для


гри користувача з комп'ютерним опонентом. Для реалізації стратегії гри
комп'ютерного опонента використовувати алгоритм альфа-бета-відсікань.
Реалізувати три рівні складності (легкий, середній, складний) + 1балл.
Зробити узагальнений висновок з лабораторної роботи.
Таблиця 2.1 – Варіанти

№ Варіант
1 Баше https://ru.wikipedia.org/wiki/Баше_(игра)

2 Нейтріко http://www.iggamecenter.com/info/ru/neutreeko.html

3 Точки https://ru.wikipedia.org/wiki/Точки_(игра)

4 Dots and Boxes https://ru.wikipedia.org/wiki/Палочки_(игра)

5 Алемунгула http://www.iggamecenter.com/info/ru/alemungula.html

6 Snakes http://www.papg.com/show?3AE4

7 Cram https://en.wikipedia.org/wiki/Cram_(game)

8 Тіко http://www.iggamecenter.com/info/ru/teeko.html

9 Obstruction 8х8 http://www.papg.com/show?2XMX

10 Gale http://www.papg.com/show?1TPI

11 Гомоку https://ru.wikipedia.org/wiki/Гомоку

12 Нім https://ru.wikipedia.org/wiki/Ним_(игра)

13 Клоббер http://www.iggamecenter.com/info/ru/clobber.html

14 Hackenbush http://www.papg.com/show?1TMP

15 Лінкідж (більше) http://www.iggamecenter.com/info/ru/linkage.html

16 Заєць и Вовки (за Зайця) http://www.iggamecenter.com/info/ru/foxh.html

17 3D Noughts and Crosses 4 x 4 x 4 http://www.papg.com/show?1TND

18 Domineering 8х8 http://www.papg.com/show?1TX6

19 Транспозиція http://www.iggamecenter.com/info/ru/transposition.html

4
20 Заєць и Вовки (за Вовків) http://www.iggamecenter.com/info/ru/foxh.html

21 Вари http://www.iggamecenter.com/info/ru/oware.html

22 Калах http://www.iggamecenter.com/info/ru/kalah.html

23 Реверсі https://ru.wikipedia.org/wiki/Реверси

24 Лінкідж (менше) http://www.iggamecenter.com/info/ru/linkage.html

25 Три мушкетери (за мушкетерів) http://www.iggamecenter.com/info/ru/threemusk.html

26 Сим https://ru.wikipedia.org/wiki/Сим_(игра)

27 Col http://www.papg.com/show?2XLY

28 Snort http://www.papg.com/show?2XM1

29 Chomp http://www.papg.com/show?3AEA

30 Три мушкетери (за гвардейців) http://www.iggamecenter.com/info/ru/threemusk.html

5
3 ВИКОНАННЯ

3.1 Програмна реалізація алгоритму

3.1.1 Вихідний код


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Runtime.InteropServices;
using System.ComponentModel;

namespace neutreekoCSHARP
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
bool isPawnSelected = false;
byte selectedPawnRow;
byte selectedPawnColumn;
int[,] field;

UIGame game = new UIGame();

public event PropertyChangedEventHandler PropertyChanged;

public MainWindow()
{
InitializeComponent();
AllocConsole();
var pawnList = new List<Pawn>{
new Pawn(){Style = (Style)Resources["FirstPlayerPawn"], GridColumn = 2, GridRow = 1},
new Pawn(){Style = (Style)Resources["FirstPlayerPawn"], GridColumn = 1, GridRow = 4},
new Pawn(){Style = (Style)Resources["FirstPlayerPawn"], GridColumn = 3, GridRow = 4},
new Pawn(){Style = (Style)Resources["SecondPlayerPawn"], GridColumn = 3, GridRow = 0},
new Pawn(){Style = (Style)Resources["SecondPlayerPawn"], GridColumn = 1, GridRow = 0},
new Pawn(){Style = (Style)Resources["SecondPlayerPawn"], GridColumn = 2, GridRow = 3},
};
Pawns.ItemsSource = pawnList;
}

[DllImport("kernel32.dll", SetLastError = true)]


internal static extern int AllocConsole();

private void Button_Click(object sender, RoutedEventArgs e)


{
new BoardOperations(field).displayBoard();
field = game.AIMove();
ShowBoard(field);
}

private void OnPawnSelect(object sender, RoutedEventArgs e)

6
{
Console.WriteLine("Hoorah!");
isPawnSelected = true;
selectedPawnColumn = ((e.Source as Button).DataContext as Pawn).GridColumn;
selectedPawnRow = ((e.Source as Button).DataContext as Pawn).GridRow;
}

private void Button_KeyDown(object sender, KeyEventArgs e)


{
Console.WriteLine("Key Down!");
if (isPawnSelected && e.Key.ToString().Contains("NumPad"))
{
Console.WriteLine("Pawn Selected");
var direction = (byte)e.Key.ToString()[6] - (byte)'0';
field = game.turn(selectedPawnRow, selectedPawnColumn, direction);
new BoardOperations(field).displayBoard();
ShowBoard(field);
}
isPawnSelected=false;
}

private void ShowBoard(int[,] board)


{
var newPawnList = new List<Pawn>();
for (int i = 0; i < board.GetLength(0); i++)
{
for (int j = 0; j < board.GetLength(1); j++)
{
if(board[i,j] == 1)
newPawnList.Add(new Pawn() { GridRow = (byte)i, GridColumn = (byte)j, Style =
(Style)Resources["FirstPlayerPawn"]});
else if (board[i, j] == 2)
newPawnList.Add(new Pawn() { GridRow = (byte)i, GridColumn = (byte)j, Style =
(Style)Resources["SecondPlayerPawn"] });
}
}
Pawns.ItemsSource = newPawnList;
PropertyChanged?.Invoke(this, new(nameof(Pawns)));
PropertyChanged?.Invoke(this, new(nameof(Pawn.GridRow)));
PropertyChanged?.Invoke(this, new(nameof(Pawn.GridColumn)));
}
}

public class Pawn


{
public System.Windows.Style Style { get; set; }

public byte GridRow { get; set; }

public byte GridColumn { get; set; }


}
}
<Window x:Class="neutreekoCSHARP.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:neutreekoCSHARP"
mc:Ignorable="d"
Title="MainWindow" Height="800" Width="800">

<Window.Resources>

<Style TargetType="Ellipse"
x:Key="FirstPlayerPawn">

7
<Setter Property="Fill">
<Setter.Value>
<SolidColorBrush Color="Black"/>
</Setter.Value>
</Setter>

<Setter Property="Stretch" Value="Fill"/>

</Style>

<Style TargetType="Ellipse"
x:Key="SecondPlayerPawn">

<Setter Property="Fill">
<Setter.Value>
<SolidColorBrush Color="White"/>
</Setter.Value>
</Setter>

<Setter Property="Stretch" Value="Fill"/>

</Style>

</Window.Resources>

<Grid ShowGridLines="True">

<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="3*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="3*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>

<StackPanel Grid.Row="0" Grid.Column="1">


<Button Click="Button_Click">
AI turn!
</Button>
</StackPanel>

<ItemsControl Name="Pawns"
Grid.Row="1"
Grid.Column="1">

<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>

<Grid
ShowGridLines="True"
Name="GameGrid"
Background="Gray">

<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
8
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
</Grid>

</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>

<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding GridRow}" />
<Setter Property="Grid.Column" Value="{Binding GridColumn}" />
</Style>
</ItemsControl.ItemContainerStyle>

<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Click="OnPawnSelect"
KeyDown="Button_KeyDown">
<Button.Template>
<ControlTemplate>
<Ellipse Stroke="Black"
StrokeThickness="3"
Style="{Binding Style}"/>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True"/>
</ControlTemplate.Triggers>
</ControlTemplate>
</Button.Template>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>

</ItemsControl>

</Grid>
</Window>
// Include namespace system

using System.Windows;

[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
<Application x:Class="neutreekoCSHARP.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:neutreekoCSHARP"
StartupUri="MainWindow.xaml">
<Application.Resources>

</Application.Resources>

9
</Application>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace neutreekoCSHARP
{
public class UIGame : Game
{
public int[,] turn(int pawnRow, int pawnColumn, int direction)
{
var ai = new AI(this);
ai.game_.currentPlayer_ = 1;
playUserMove(pawnRow, pawnColumn, direction);
new BoardOperations(ai.game_.board_).displayBoard();
if (hasWon(currentPlayer_))
Console.WriteLine("Game over. Player\'s " + currentPlayer_.ToString() + " victory.");

return ai.game_.board_;
}

public int[,] AIMove()


{
var ai = new AI(this);
ai.game_.currentPlayer_ = 2;
ai.playBestMove();

if (hasWon(currentPlayer_))
Console.WriteLine("Game over. Player\'s " + currentPlayer_.ToString() + " victory.");

return ai.game_.board_;
}
}
}
// Include namespace system

namespace neutreekoCSHARP
{
public abstract class Strategy
{
//Turn on which the strategy is applied.
public Move currentMove_;
public abstract int run();

public virtual bool shouldExitNow()


{
return false;
}
}
}
// Include namespace system

namespace neutreekoCSHARP
{
public sealed class Settings
{
/**
* Choice of two opponents.
* ie. The game can be multiplayer, human-AI, or AI-AI.
*/
public
const PlayerType PLAYER_1 = PlayerType.HUMAN;
public
const PlayerType PLAYER_2 = PlayerType.AI;
10
/**
* Maximum depth of recursive analysis.
* ie. Number of hits the AI will see ahead of time.
*/
public
const int MAX_MINMAX_DEPTH = 7;
/**
* Maximum depth of the recursive count of the number of possible errors.
* The number must be less than or equal to the depth of the assessment.
* ie. The AI will count the number of fatal errors that the opponent can
* do in the next round.
*/
public
const int MAX_OPENINGS_DEPTH = 4;
}
}
// Include namespace system

namespace neutreekoCSHARP
{
public enum PlayerType
{
AI, HUMAN
}
}
// Include namespace system

namespace neutreekoCSHARP
{
public class Move
{
public Coords position;
public Coords direction;
public Move(Coords position, Coords direction)
{
this.position = position;
this.direction = direction;
}
}
}
// Include namespace system
using System;

namespace neutreekoCSHARP
{
public class MinMax
{
public Game game_;
public AI ai_;
public int score_;
public int nOpenings_;
public MinMax(AI ai)
{
game_ = ai.game_;
ai_ = ai;
}
public int run()
{
if (game_.hasWon(game_.currentPlayer_))
return 1000;

var min = new StrategyForMin(1001, -1001, 0, game_, ai_);


score_ = ai_.forEachPossibleMove(min, game_.getOpponent());
nOpenings_ = min.nOpenings_;
return score_;
}
11
private abstract class MinMaxStrategy : Strategy
{
public int minScore_;
public int maxScore_;
public int depth_;
public int player_;
public MinMaxStrategy(int minScore, int maxScore, int depth)
{
minScore_ = minScore;
maxScore_ = maxScore;
depth_ = depth;
}
}

private class StrategyForMax : MinMaxStrategy


{
private readonly Game game_;
private readonly AI ai_;

public StrategyForMax(int minScore, int maxScore, int depth, Game game_, AI ai_)
:base (minScore, maxScore, depth)
{
player_ = game_.getOpponent();
this.game_ = game_;
this.ai_ = ai_;
}

public override int run()


{
if (game_.hasWon(player_))
{
return (-1000 + depth_);
}
if (depth_ == Settings.MAX_MINMAX_DEPTH)
{
return 0;
}
var min = new StrategyForMin(minScore_, maxScore_, depth_ + 1, game_, ai_);
maxScore_ = Math.Max(maxScore_, ai_.forEachPossibleMove(min, player_));
return maxScore_;
}

public override bool shouldExitNow()


{
return depth_ >= Settings.MAX_OPENINGS_DEPTH && maxScore_ >= minScore_;
}
}

private class StrategyForMin : MinMaxStrategy


{
private readonly Game game_;
private readonly AI ai_;
public int nOpenings_ = 0;
public StrategyForMin(int minScore, int maxScore, int depth, Game game_, AI ai_)
:base(minScore, maxScore, depth)
{
player_ = game_.currentPlayer_;
this.game_ = game_;
this.ai_ = ai_;
}
public override int run()
{
if (game_.hasWon(player_))
{
// We won
12
return 1000 - depth_;
}
if (depth_ == Settings.MAX_MINMAX_DEPTH)
{
// Max depth reached.
return 0;
}
var max = new StrategyForMax(minScore_, maxScore_, depth_ + 1, game_, ai_);
var score = ai_.forEachPossibleMove(max, player_);
if (depth_ == 0 && score > 0)
{
nOpenings_++;
}
minScore_ = Math.Min(minScore_, score);
return minScore_;
}

public override bool shouldExitNow()


{
return depth_ >= Settings.MAX_OPENINGS_DEPTH && minScore_ <= maxScore_;
}
}
}
}
// Include namespace system
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace neutreekoCSHARP
{

public class GameService


{
public static void Start()
{
Console.WriteLine("INSTRUCTIONS");
Console.WriteLine(" Positions");
Console.WriteLine(" 0 0 0 1 ... 0 4");
Console.WriteLine(" 1 0 ... ... ...");
Console.WriteLine(" ... ... ... ...");
Console.WriteLine(" 4 0 ... ... 4 4");
Console.WriteLine(" Directions");
Console.WriteLine(" 7 8 9");
Console.WriteLine(" 4 6");
Console.WriteLine(" 1 2 3");
Console.WriteLine(" Accepted entries");
Console.WriteLine(" [0-4] [0-4] [1-46-9]");
Console.WriteLine(" $1 position.i");
Console.WriteLine(" $2 position.j");
Console.WriteLine(" $3 direction");
Console.WriteLine(" Exemple");
Console.WriteLine(" (0,1) right <=> 0 3 6\n");
// Start
var game = new AIGame();
game.start();
}
}
}
// Include namespace system
using System;

namespace neutreekoCSHARP
13
{
public abstract class Game
{
public int[,] board_ = new int[5, 5];
public BoardOperations boardOperations_;
// Coordinates of the pawns. [, player1, player2] [pawn1, pawn2, pawn3].
public Coords[,] pawns_ = new Coords[3, 3];
public int currentPlayer_ = 2;
// 8 directions possibles (NO-N-NE-O-E-SO-S-SE)
public
readonly Coords[] directions_ = new Coords[8];
public Game()
{
boardOperations_ = new BoardOperations(board_);

// Initial position of the pawns on the board.


this.board_[0, 1] = this.board_[0, 3] = this.board_[3, 2] = 1;
this.board_[1, 2] = this.board_[4, 1] = this.board_[4, 3] = 2;
this.pawns_[1, 0] = new Coords(0, 1);
this.pawns_[1, 1] = new Coords(0, 3);
this.pawns_[1, 2] = new Coords(3, 2);
this.pawns_[2, 0] = new Coords(1, 2);
this.pawns_[2, 1] = new Coords(4, 1);
this.pawns_[2, 2] = new Coords(4, 3);
// We fill the vector with 8 possible directions.
var k = 0;
for (var i = -1; i <= 1; i++)
{
for (var j = -1; j <= 1; j++)
{
if (i != 0 || j != 0)
{
this.directions_[k++] = new Coords(i, j);
}
}
}
}

public bool hasWon(int player)


{
for (var i = 0; i < 3; i++)
{
// Every possible pawn.
foreach ( Coords direction in this.directions_)
{
// Each direction.
// We see if the following two boxes in this direction
// contain the other two pawns.
var square2 = this.pawns_[player, i].add(direction);
var square3 = square2.add(direction);
if (square3.isOnBoard())
{
// If we are still in the field.
if (this.board_[square2.i, square2.j] == player &&
this.board_[square3.i, square3.j] == player)
{
return true;
}
}
}
}
return false;
}

public Coords move(Move move)


{
14
// Player owning the pawn.
var oldPosition = move.position;
var player = this.board_[oldPosition.i, oldPosition.j];
var newPosition = oldPosition.clone();
// We move the pawn in the direction as long as we can.
while (this.boardOperations_.isValidMove(new Move(newPosition, move.direction)))
{
newPosition.addAssign(move.direction);
}
// We found the final position. We put the pawn there.
this.boardOperations_.swap(newPosition, oldPosition);
// We update the coordinates of the player's pawns.
for (var i = 0; i < 3; i++)
{
if (this.pawns_[player, i].equals(oldPosition))
{
this.pawns_[player, i] = newPosition;
break;
}
}
return newPosition;
}

public void playUserMove()


{
while (true)
{
var i = Convert.ToInt64(Console.ReadLine());
var j = Convert.ToInt64(Console.ReadLine());
var direction = Convert.ToInt64(Console.ReadLine());
var isValidDirection = true;
if (direction >= 1 && direction <= 3)
{
direction += 4;
}
else if (direction == 4)
{
direction--;
}
else if (direction == 6)
{
direction -= 2;
}
else if (direction >= 7 && direction <= 9)
{
direction -= 7;
}
else
{
isValidDirection = false;
}
var position = new Coords((int)i, (int)j);

if (isValidDirection)
{
if (position.isOnBoard())
{
if (this.board_[i, j] == this.currentPlayer_)
{
var move = new Move(position, this.directions_[direction]);
if (this.boardOperations_.isValidMove(move))
{
this.move(move);
return;
}

15
else
{
Console.WriteLine("Direction invalid");
}
}
else
{
Console.WriteLine("No pawns available");
}
}
else
{
Console.WriteLine("Position invalid");
}
}
else
{
Console.WriteLine("Direction invalid");
}
}
}

public void playUserMove(int i, int j, int direction)


{
while (true)
{
var isValidDirection = true;
if (direction >= 1 && direction <= 3)
{
direction += 4;
}
else if (direction == 4)
{
direction--;
}
else if (direction == 6)
{
direction -= 2;
}
else if (direction >= 7 && direction <= 9)
{
direction -= 7;
}
else
{
isValidDirection = false;
}
var position = new Coords((int)i, (int)j);

if (isValidDirection)
{
if (position.isOnBoard())
{
if (this.board_[i, j] == this.currentPlayer_)
{
var move = new Move(position, this.directions_[direction]);
if (this.boardOperations_.isValidMove(move))
{
this.move(move);
return;
}
else
{
Console.WriteLine("Direction invalid");
}
}
16
else
{
Console.WriteLine("No pawns available");
}
}
else
{
Console.WriteLine("Position invalid");
}
}
else
{
Console.WriteLine("Direction invalid");
}
}
}

public int getOpponent()


{
return (this.currentPlayer_ % 2) + 1;
}
}
}
// Include namespace system

namespace neutreekoCSHARP
{
public class Coords
{
public int i;
public int j;
public Coords(int i, int j)
{
this.i = i;
this.j = j;
}
public Coords add(Coords coords)
{
return new Coords(this.i + coords.i, this.j + coords.j);
}
public void addAssign(Coords coords)
{
this.i += coords.i;
this.j += coords.j;
}
public Coords clone()
{
return new Coords(i, j);
}
public bool equals(Coords coords)
{
return this.i == coords.i && this.j == coords.j;
}
public bool isOnBoard()
{
return this.i >= 0 && this.i < 5 && this.j >= 0 && this.j < 5;
}
}
}
// Include namespace system
using System;

namespace neutreekoCSHARP
{
public class BoardOperations
{
17
private int[,] board_;
public BoardOperations(int[,] board)
{
board_ = board;
}
public void displayBoard()
{
for (var i = 0; i < 5; i++)
{
for (var j = 0; j < 5; j++)
{
if (board_[i, j] != 0)
Console.Write(board_[i, j].ToString() + " ");
else
Console.Write("- ");
}
Console.WriteLine();
}
Console.WriteLine();
}
public void swap(Coords a, Coords b)
{
var tmp = board_[a.i, a.j];
board_[a.i, a.j] = board_[b.i, b.j];
board_[b.i, b.j] = tmp;
}
public bool isValidMove(Move move)
{
var newPosition = move.position.add(move.direction);
if (newPosition.isOnBoard())
{
return board_[newPosition.i, newPosition.j] == 0;
}
return false;
}
}
}
// Include namespace system
using System;

namespace neutreekoCSHARP
{
/**
Introduces an AI with a min-max algorithm.
*/
public class AIGame : Game
{
public void start()
{
// Initialisation.
var ai = new AI(this);
// Main loop.
do
{
currentPlayer_ = getOpponent();
boardOperations_.displayBoard();
Console.WriteLine("Player " + currentPlayer_.ToString() + " : ");
var player = (currentPlayer_ == 1) ? Settings.PLAYER_1 : Settings.PLAYER_2;
if (player == PlayerType.AI)
{
ai.playBestMove();
}
else
{
playUserMove();
}
18
} while (!hasWon(currentPlayer_));
boardOperations_.displayBoard();
Console.WriteLine("Game over. Player\'s " + currentPlayer_.ToString() + " victory.");
}
}
}
// Include namespace system
using System;

namespace neutreekoCSHARP
{
public class AI
{
public Game game_;
public AI(Game game)
{
game_ = game;
}
public int forEachPossibleMove(Strategy strategy, int player)
{
var res = 0;
// For each piece
for (var i = 0; i < 3; i++)
{
var oldPosition = game_.pawns_[player, i];
// foreach valid move
foreach (Coords direction in game_.directions_)
{
var move = new Move(oldPosition, direction);
if (game_.boardOperations_.isValidMove(move))
{
Coords newPosition = game_.move(move);

strategy.currentMove_ = move;

res = strategy.run();

game_.pawns_[player, i] = oldPosition;
game_.boardOperations_.swap(newPosition, oldPosition);

if (strategy.shouldExitNow())
{
return res;
}
}
}
}
return res;
}
public void playBestMove()
{
// Initialisation.
var strategy = new FindBestMoveStrategy(this);
// We apply the min-max algorithm.
forEachPossibleMove(strategy, game_.currentPlayer_);
// Interpretation
if (strategy.maxScore > 0)
{
Console.WriteLine("I'm going to win...\n");
}
else if (strategy.maxScore < 0)
{
Console.WriteLine("You can win ...\n");
}

19
// Finally, we make the best move.
game_.move(strategy.bestMove);
}
private class FindBestMoveStrategy : Strategy
{
public FindBestMoveStrategy(AI ai)
{
this.ai = ai;
}
public int maxScore = -1001;
public Move bestMove = null;
private int maxNOpenings = 0;
private readonly AI ai;

public override int run()


{
var minMax = new MinMax(ai);
// We evaluate the move according to the victory / defeat criterion.
var score = minMax.run();
// We update the best possible move.
if (score >= maxScore)
{
// In the event of a tie, we choose a move aiming to maximize the possibilities
// opponent's error.
var nOpenings = minMax.nOpenings_;
if (score > maxScore || (score == maxScore && nOpenings > maxNOpenings))
{
maxScore = score;
bestMove = currentMove_;
maxNOpenings = nOpenings;
}
}
return -1;
}
}
}
}

3.1.2 Приклади роботи

На рисунках 3.1 і 3.2 показані приклади роботи програми.

20
Рисунок 3.1 –

Рисунок 3.2 –

3.2

21
ВИСНОВОК

В рамках даної лабораторної роботи ми вивчили алгоритм Min-max для


розробки віртуального інтелекту для відеоігор. Реалізували застосунок для гри
в Neutreeko та візуальну репрезентацію гри

Посилання на гітхаб: https://github.com/yan14171/PALAB5

22
КРИТЕРІЇ ОЦІНЮВАННЯ

При здачі лабораторної роботи до 10.12.2020 включно максимальний бал


дорівнює – 5. Після 10.12.2020 максимальний бал дорівнює – 1.
Критерії оцінювання у відсотках від максимального балу:
 програмна реалізація алгоритму – 95%;
 висновок – 5%.
+1 додатковий бал можна отримати за реалізацію рівнів складності.

23

You might also like