mirror of
https://github.com/TwoFX/Morris.git
synced 2025-12-13 08:22:51 +00:00
Add controller, external assembly loading and other fancy things
This commit is contained in:
41
ExternalBotTest/AssemblyInfo.fs
Normal file
41
ExternalBotTest/AssemblyInfo.fs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
namespace ExternalBotTest.AssemblyInfo
|
||||||
|
|
||||||
|
open System.Reflection
|
||||||
|
open System.Runtime.CompilerServices
|
||||||
|
open System.Runtime.InteropServices
|
||||||
|
|
||||||
|
// General Information about an assembly is controlled through the following
|
||||||
|
// set of attributes. Change these attribute values to modify the information
|
||||||
|
// associated with an assembly.
|
||||||
|
[<assembly: AssemblyTitle("ExternalBotTest")>]
|
||||||
|
[<assembly: AssemblyDescription("")>]
|
||||||
|
[<assembly: AssemblyConfiguration("")>]
|
||||||
|
[<assembly: AssemblyCompany("")>]
|
||||||
|
[<assembly: AssemblyProduct("ExternalBotTest")>]
|
||||||
|
[<assembly: AssemblyCopyright("Copyright © 2016")>]
|
||||||
|
[<assembly: AssemblyTrademark("")>]
|
||||||
|
[<assembly: AssemblyCulture("")>]
|
||||||
|
|
||||||
|
// Setting ComVisible to false makes the types in this assembly not visible
|
||||||
|
// to COM components. If you need to access a type in this assembly from
|
||||||
|
// COM, set the ComVisible attribute to true on that type.
|
||||||
|
[<assembly: ComVisible(false)>]
|
||||||
|
|
||||||
|
// The following GUID is for the ID of the typelib if this project is exposed to COM
|
||||||
|
[<assembly: Guid("712da2c7-1b88-4ddb-8b79-46af5d5f6b19")>]
|
||||||
|
|
||||||
|
// Version information for an assembly consists of the following four values:
|
||||||
|
//
|
||||||
|
// Major Version
|
||||||
|
// Minor Version
|
||||||
|
// Build Number
|
||||||
|
// Revision
|
||||||
|
//
|
||||||
|
// You can specify all the values or you can default the Build and Revision Numbers
|
||||||
|
// by using the '*' as shown below:
|
||||||
|
// [<assembly: AssemblyVersion("1.0.*")>]
|
||||||
|
[<assembly: AssemblyVersion("1.0.0.0")>]
|
||||||
|
[<assembly: AssemblyFileVersion("1.0.0.0")>]
|
||||||
|
|
||||||
|
do
|
||||||
|
()
|
||||||
76
ExternalBotTest/ExternalBotTest.fsproj
Normal file
76
ExternalBotTest/ExternalBotTest.fsproj
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<SchemaVersion>2.0</SchemaVersion>
|
||||||
|
<ProjectGuid>712da2c7-1b88-4ddb-8b79-46af5d5f6b19</ProjectGuid>
|
||||||
|
<OutputType>Library</OutputType>
|
||||||
|
<RootNamespace>ExternalBotTest</RootNamespace>
|
||||||
|
<AssemblyName>ExternalBotTest</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
|
||||||
|
<TargetFSharpCoreVersion>4.4.0.0</TargetFSharpCoreVersion>
|
||||||
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
<Name>ExternalBotTest</Name>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<Tailcalls>false</Tailcalls>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<WarningLevel>3</WarningLevel>
|
||||||
|
<DocumentationFile>bin\Debug\ExternalBotTest.XML</DocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<Tailcalls>true</Tailcalls>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<WarningLevel>3</WarningLevel>
|
||||||
|
<DocumentationFile>bin\Release\ExternalBotTest.XML</DocumentationFile>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="Morris">
|
||||||
|
<HintPath>D:\Users\marku\Documents\Visual Studio 2015\Projects\Morris\Morris\bin\Debug\Morris.exe</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="mscorlib" />
|
||||||
|
<Reference Include="FSharp.Core, Version=$(TargetFSharpCoreVersion), Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||||
|
<Private>True</Private>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Numerics" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="AssemblyInfo.fs" />
|
||||||
|
<Compile Include="FSharpRandomBot.fs" />
|
||||||
|
<None Include="Script.fsx" />
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup>
|
||||||
|
<MinimumVisualStudioVersion Condition="'$(MinimumVisualStudioVersion)' == ''">11</MinimumVisualStudioVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Choose>
|
||||||
|
<When Condition="'$(VisualStudioVersion)' == '11.0'">
|
||||||
|
<PropertyGroup Condition="Exists('$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets')">
|
||||||
|
<FSharpTargetsPath>$(MSBuildExtensionsPath32)\..\Microsoft SDKs\F#\3.0\Framework\v4.0\Microsoft.FSharp.Targets</FSharpTargetsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
</When>
|
||||||
|
<Otherwise>
|
||||||
|
<PropertyGroup Condition="Exists('$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets')">
|
||||||
|
<FSharpTargetsPath>$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\FSharp\Microsoft.FSharp.Targets</FSharpTargetsPath>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Otherwise>
|
||||||
|
</Choose>
|
||||||
|
<Import Project="$(FSharpTargetsPath)" />
|
||||||
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
|
<Target Name="BeforeBuild">
|
||||||
|
</Target>
|
||||||
|
<Target Name="AfterBuild">
|
||||||
|
</Target>
|
||||||
|
-->
|
||||||
|
</Project>
|
||||||
28
ExternalBotTest/FSharpRandomBot.fs
Normal file
28
ExternalBotTest/FSharpRandomBot.fs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
(*
|
||||||
|
* FSharpRandomBot.fs
|
||||||
|
* Copyright (c) 2016 Markus Himmel
|
||||||
|
* This file is distributed under the terms of the MIT license
|
||||||
|
*)
|
||||||
|
|
||||||
|
namespace ExternalBotTest
|
||||||
|
|
||||||
|
open Morris
|
||||||
|
|
||||||
|
[<SelectorName("F# KI")>]
|
||||||
|
type FSharpRandomBot() =
|
||||||
|
|
||||||
|
let rng = System.Random ()
|
||||||
|
let chooseRandom n = Seq.item (Seq.length n |> rng.Next) n
|
||||||
|
|
||||||
|
interface IMoveProvider with
|
||||||
|
// Funktioniert exakt genauso wie das C#-Pendant
|
||||||
|
member this.GetNextMove state =
|
||||||
|
let chosen = state.BasicMoves () |> chooseRandom
|
||||||
|
|
||||||
|
match state.IsValidMove chosen with
|
||||||
|
| MoveValidity.ClosesMill ->
|
||||||
|
[0..GameState.FIELD_SIZE - 1]
|
||||||
|
|> Seq.where (fun d -> int state.Board.[d] = int (state.NextToMove.Opponent()))
|
||||||
|
|> chooseRandom
|
||||||
|
|> chosen.WithRemove
|
||||||
|
| _ -> chosen
|
||||||
8
ExternalBotTest/Script.fsx
Normal file
8
ExternalBotTest/Script.fsx
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
// Learn more about F# at http://fsharp.org. See the 'F# Tutorial' project
|
||||||
|
// for more guidance on F# programming.
|
||||||
|
|
||||||
|
#load "Library1.fs"
|
||||||
|
open ExternalBotTest
|
||||||
|
|
||||||
|
// Define your library scripting code here
|
||||||
|
|
||||||
@@ -5,6 +5,8 @@ VisualStudioVersion = 14.0.25420.1
|
|||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Morris", "Morris\Morris.csproj", "{E3CCB2E8-5840-4442-8A66-177F5DF4C4F5}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Morris", "Morris\Morris.csproj", "{E3CCB2E8-5840-4442-8A66-177F5DF4C4F5}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "ExternalBotTest", "ExternalBotTest\ExternalBotTest.fsproj", "{712DA2C7-1B88-4DDB-8B79-46AF5D5F6B19}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -15,6 +17,10 @@ Global
|
|||||||
{E3CCB2E8-5840-4442-8A66-177F5DF4C4F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{E3CCB2E8-5840-4442-8A66-177F5DF4C4F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{E3CCB2E8-5840-4442-8A66-177F5DF4C4F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{E3CCB2E8-5840-4442-8A66-177F5DF4C4F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{E3CCB2E8-5840-4442-8A66-177F5DF4C4F5}.Release|Any CPU.Build.0 = Release|Any CPU
|
{E3CCB2E8-5840-4442-8A66-177F5DF4C4F5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{712DA2C7-1B88-4DDB-8B79-46AF5D5F6B19}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{712DA2C7-1B88-4DDB-8B79-46AF5D5F6B19}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{712DA2C7-1B88-4DDB-8B79-46AF5D5F6B19}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{712DA2C7-1B88-4DDB-8B79-46AF5D5F6B19}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|||||||
@@ -13,7 +13,8 @@ namespace Morris
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ermöglicht Eingabe und Ausgabe der Spielsituation auf der Konsole.
|
/// Ermöglicht Eingabe und Ausgabe der Spielsituation auf der Konsole.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class ConsoleInteraction : IGameStateObserver, IMoveProvider
|
[SelectorName("Konsole"), SingleInstance]
|
||||||
|
internal class ConsoleInteraction : IGameStateObserver, IMoveProvider
|
||||||
{
|
{
|
||||||
public ConsoleInteraction()
|
public ConsoleInteraction()
|
||||||
{
|
{
|
||||||
|
|||||||
23
Morris/Controller.xaml
Normal file
23
Morris/Controller.xaml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<Window x:Class="Morris.Controller"
|
||||||
|
x:ClassModifier="internal"
|
||||||
|
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:Morris"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Title="Morris" Height="332.409" Width="285.955" Closed="Window_Closed">
|
||||||
|
<Grid>
|
||||||
|
<ComboBox x:Name="whiteBox" HorizontalAlignment="Left" Margin="54,35,0,0" VerticalAlignment="Top" Width="194" SelectionChanged="white_SelectionChanged"/>
|
||||||
|
<ComboBox x:Name="blackBox" HorizontalAlignment="Left" Margin="71,62,0,0" VerticalAlignment="Top" Width="177" SelectionChanged="black_SelectionChanged"/>
|
||||||
|
<Label Content="Weiß:" HorizontalAlignment="Left" Margin="10,35,0,0" VerticalAlignment="Top"/>
|
||||||
|
<Label Content="Schwarz:" HorizontalAlignment="Left" Margin="10,62,0,0" VerticalAlignment="Top"/>
|
||||||
|
<ListBox x:Name="displayBox" HorizontalAlignment="Left" Height="108" Margin="10,115,0,0" VerticalAlignment="Top" Width="238" SelectionMode="Multiple" SelectionChanged="displayBox_SelectionChanged"/>
|
||||||
|
<Button x:Name="newGame" Content="Neues Spiel" HorizontalAlignment="Left" Margin="124,10,0,0" VerticalAlignment="Top" Width="124" Height="20" Click="newGame_Click"/>
|
||||||
|
<Label x:Name="label" Content="Anzeigen:" HorizontalAlignment="Left" Margin="10,89,0,0" VerticalAlignment="Top" RenderTransformOrigin="0.553,-0.462"/>
|
||||||
|
<Button x:Name="loadAssembly" Content="Assembly laden..." HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="109" Click="loadAssembly_Click"/>
|
||||||
|
<Slider x:Name="delay" HorizontalAlignment="Left" Margin="10,254,0,0" VerticalAlignment="Top" Width="238" Maximum="2000" SmallChange="1" TickFrequency="100" TickPlacement="BottomRight" ValueChanged="delay_ValueChanged"/>
|
||||||
|
<Label x:Name="label1" Content="Verzögerung:" HorizontalAlignment="Left" Margin="10,228,0,0" VerticalAlignment="Top"/>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Window>
|
||||||
220
Morris/Controller.xaml.cs
Normal file
220
Morris/Controller.xaml.cs
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
/*
|
||||||
|
* Controller.xaml.cs
|
||||||
|
* Copyright (c) 2016 Markus Himmel
|
||||||
|
* This file is distributed under the terms of the MIT license
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
|
||||||
|
namespace Morris
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gibt dem Benutzer die Möglichkeit, ein Mühlespiel zu kontrollieren
|
||||||
|
/// </summary>
|
||||||
|
internal partial class Controller : Window
|
||||||
|
{
|
||||||
|
public Controller()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
|
||||||
|
populateLists(AppDomain.CurrentDomain.GetAssemblies());
|
||||||
|
|
||||||
|
whiteBox.ItemsSource = blackBox.ItemsSource = players;
|
||||||
|
displayBox.ItemsSource = displays;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Das aktuelle Spiel und der Thread, auf dem es läuft
|
||||||
|
private Game theGame;
|
||||||
|
private Thread gameThread;
|
||||||
|
|
||||||
|
// Die Objekte, die die ComboBoxen und die ListBox nehmen und wieder zurückgeben
|
||||||
|
private ObservableCollection<SelectorType> players = new ObservableCollection<SelectorType>();
|
||||||
|
private ObservableCollection<SelectorType> displays = new ObservableCollection<SelectorType>();
|
||||||
|
|
||||||
|
// Instanzen, die gecacht werden, weil es von dem Typen nur eine Instanz geben darf
|
||||||
|
private Dictionary<Type, object> singleInstances = new Dictionary<Type, object>();
|
||||||
|
|
||||||
|
// Instanzen, die gecacht werden, weil das Display wieder abgewählt werden kann und wir die Instanz
|
||||||
|
// brauchen, um Game.RemoveObserver damit aufzurufen.
|
||||||
|
private Dictionary<Type, IGameStateObserver> displayObjects = new Dictionary<Type, IGameStateObserver>();
|
||||||
|
|
||||||
|
// Fügt alle relevatenten Typen in den angegebenen Assemblies in die entsprechenden Listen ein
|
||||||
|
private void populateLists(IEnumerable<Assembly> assemblies)
|
||||||
|
{
|
||||||
|
foreach (var type in getTypes(typeof(IMoveProvider), assemblies))
|
||||||
|
players.Add(type);
|
||||||
|
|
||||||
|
foreach (var type in getTypes(typeof(IGameStateObserver), assemblies))
|
||||||
|
displays.Add(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gibt eine Instanz des angegebenen Typen unter Beachtung der
|
||||||
|
// Möglichkeit, dass der Typ das SingleInstanceAttribute haben kann,
|
||||||
|
// zurück.
|
||||||
|
private object getInstance(SelectorType selectorType)
|
||||||
|
{
|
||||||
|
Type type = selectorType.Type;
|
||||||
|
|
||||||
|
// Anmerkung: Wir wissen an dieser Stelle sicher, dass type einen
|
||||||
|
// parameterlosen Konstruktor hat
|
||||||
|
|
||||||
|
// Für jede Verwendung eine neue Instanz?
|
||||||
|
if (type.GetCustomAttribute<SingleInstanceAttribute>() == null)
|
||||||
|
return Activator.CreateInstance(type);
|
||||||
|
|
||||||
|
// C# 7 bekommt out variables, dann wird das schöner
|
||||||
|
object result;
|
||||||
|
if (singleInstances.TryGetValue(type, out result))
|
||||||
|
return result;
|
||||||
|
|
||||||
|
return singleInstances[type] = Activator.CreateInstance(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gibt alle Typen in assemblies zurück, die @interface implementieren und einen parameterlosen Konstruktor haben
|
||||||
|
private IEnumerable<SelectorType> getTypes(Type @interface, IEnumerable<Assembly> assemblies)
|
||||||
|
{
|
||||||
|
return assemblies
|
||||||
|
.SelectMany(s => s.GetTypes())
|
||||||
|
.Where(type => type.IsClass && type.GetInterfaces().Contains(@interface) && type.GetConstructor(Type.EmptyTypes) != null)
|
||||||
|
.Select(type => new SelectorType(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Versucht, ein neues Display zu registrieren
|
||||||
|
private void tryAddDisplay(SelectorType type)
|
||||||
|
{
|
||||||
|
if (!displayObjects.ContainsKey(type.Type))
|
||||||
|
{
|
||||||
|
// Objekt muss noch erstellt werden
|
||||||
|
IGameStateObserver o = getInstance(type) as IGameStateObserver;
|
||||||
|
if (o == null)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"Anzeige {type} konnte nicht erstellt werden.");
|
||||||
|
}
|
||||||
|
displayObjects[type.Type] = o;
|
||||||
|
}
|
||||||
|
// Objekt existiert jetzt sicher, registrieren
|
||||||
|
theGame.AddObserver(displayObjects[type.Type]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tryRemoveDisplay(SelectorType type)
|
||||||
|
{
|
||||||
|
IGameStateObserver obs;
|
||||||
|
if (!displayObjects.TryGetValue(type.Type, out obs))
|
||||||
|
return;
|
||||||
|
|
||||||
|
theGame.RemoveObserver(obs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extrahiert einen IMoveProvider aus der ComboBox-Auswahl
|
||||||
|
private IMoveProvider getFromBox(ComboBox source)
|
||||||
|
{
|
||||||
|
var Type = source.SelectedItem as SelectorType;
|
||||||
|
|
||||||
|
if (Type == null)
|
||||||
|
{
|
||||||
|
MessageBox.Show("Bitte Spieler auswählen.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
IMoveProvider prov = getInstance(Type) as IMoveProvider;
|
||||||
|
if (prov == null)
|
||||||
|
{
|
||||||
|
MessageBox.Show("Spieler konnte nicht erstellt werden.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return prov;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Neues Spiel
|
||||||
|
private void newGame_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
// Altes Spiel terminieren
|
||||||
|
if (gameThread != null)
|
||||||
|
gameThread.Abort();
|
||||||
|
|
||||||
|
var white = getFromBox(whiteBox);
|
||||||
|
var black = getFromBox(blackBox);
|
||||||
|
|
||||||
|
if (white == null || black == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
theGame = new Game(white, black, (int)delay.Value);
|
||||||
|
|
||||||
|
foreach (SelectorType type in displayBox.SelectedItems)
|
||||||
|
{
|
||||||
|
tryAddDisplay(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
gameThread = new Thread(() => theGame.Run());
|
||||||
|
gameThread.Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void white_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (theGame != null)
|
||||||
|
theGame.White = getFromBox(whiteBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void black_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (theGame != null)
|
||||||
|
theGame.Black = getFromBox(blackBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void displayBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (theGame == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var item in e.AddedItems.OfType<SelectorType>())
|
||||||
|
tryAddDisplay(item);
|
||||||
|
|
||||||
|
foreach (var item in e.RemovedItems.OfType<SelectorType>())
|
||||||
|
tryRemoveDisplay(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Window_Closed(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
Application.Current.Shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadAssembly_Click(object sender, RoutedEventArgs e)
|
||||||
|
{
|
||||||
|
var dialog = new OpenFileDialog()
|
||||||
|
{
|
||||||
|
Filter = "Dynamic Link Libraries (*.dll)|*.dll|Executables (*.exe)|*.exe|Alle Dateien (*.*)|*.*"
|
||||||
|
};
|
||||||
|
|
||||||
|
var result = dialog.ShowDialog();
|
||||||
|
if (result != true)
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var assembly = Assembly.LoadFrom(dialog.FileName);
|
||||||
|
if (assembly == null)
|
||||||
|
throw new Exception();
|
||||||
|
populateLists(new[] { assembly });
|
||||||
|
}
|
||||||
|
catch (Exception)
|
||||||
|
{
|
||||||
|
MessageBox.Show("Assembly konnte nicht geladen werden.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void delay_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
|
||||||
|
{
|
||||||
|
if (theGame != null)
|
||||||
|
theGame.Delay = (int)e.NewValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ namespace Morris
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Statische Klasse, die Methoden bereitstellt um Spielfeldpositionen zwischen verschiedenen Formaten zu überführen
|
/// Statische Klasse, die Methoden bereitstellt um Spielfeldpositionen zwischen verschiedenen Formaten zu überführen
|
||||||
/// </summary>
|
/// </summary>
|
||||||
static class CoordinateTranslator
|
public static class CoordinateTranslator
|
||||||
{
|
{
|
||||||
private static Dictionary<string, int> humans = new Dictionary<string, int>()
|
private static Dictionary<string, int> humans = new Dictionary<string, int>()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Morris
|
namespace Morris
|
||||||
{
|
{
|
||||||
internal static class ExtensionMethods
|
public static class ExtensionMethods
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gibt den Gegner des Spielers zurück
|
/// Gibt den Gegner des Spielers zurück
|
||||||
|
|||||||
@@ -12,13 +12,43 @@ namespace Morris
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Repräsentiert ein einzelnes Mühlespiel
|
/// Repräsentiert ein einzelnes Mühlespiel
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class Game
|
internal class Game
|
||||||
{
|
{
|
||||||
private List<IGameStateObserver> observers = new List<IGameStateObserver>();
|
private List<IGameStateObserver> observers = new List<IGameStateObserver>();
|
||||||
private GameState state;
|
private GameState state;
|
||||||
private Dictionary<Player, IMoveProvider> providers;
|
private Dictionary<Player, IMoveProvider> providers;
|
||||||
|
|
||||||
|
public IMoveProvider White
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return providers[Player.White];
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
providers[Player.White] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Game(IMoveProvider white, IMoveProvider black)
|
public IMoveProvider Black
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return providers[Player.Black];
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
providers[Player.Black] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Delay
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Game(IMoveProvider white, IMoveProvider black, int delay)
|
||||||
{
|
{
|
||||||
state = new GameState();
|
state = new GameState();
|
||||||
providers = new Dictionary<Player, IMoveProvider>()
|
providers = new Dictionary<Player, IMoveProvider>()
|
||||||
@@ -26,6 +56,7 @@ namespace Morris
|
|||||||
[Player.White] = white,
|
[Player.White] = white,
|
||||||
[Player.Black] = black
|
[Player.Black] = black
|
||||||
};
|
};
|
||||||
|
Delay = delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -35,7 +66,7 @@ namespace Morris
|
|||||||
/// erfolgreichem Zug der nächste Zug angefordert wird (damit KI vs. KI-Spiele in einem
|
/// erfolgreichem Zug der nächste Zug angefordert wird (damit KI vs. KI-Spiele in einem
|
||||||
/// angemessenen Tempo angesehen werden können)</param>
|
/// angemessenen Tempo angesehen werden können)</param>
|
||||||
/// <returns>Das Spielergebnis</returns>
|
/// <returns>Das Spielergebnis</returns>
|
||||||
public GameResult Run(int moveDelay = 0)
|
public GameResult Run()
|
||||||
{
|
{
|
||||||
notifyOberservers();
|
notifyOberservers();
|
||||||
MoveResult res;
|
MoveResult res;
|
||||||
@@ -50,7 +81,7 @@ namespace Morris
|
|||||||
} while (res == MoveResult.InvalidMove);
|
} while (res == MoveResult.InvalidMove);
|
||||||
|
|
||||||
notifyOberservers();
|
notifyOberservers();
|
||||||
Thread.Sleep(moveDelay);
|
Thread.Sleep(Delay);
|
||||||
} while (state.Result == GameResult.Running);
|
} while (state.Result == GameResult.Running);
|
||||||
|
|
||||||
return state.Result;
|
return state.Result;
|
||||||
@@ -63,6 +94,7 @@ namespace Morris
|
|||||||
public void AddObserver(IGameStateObserver observer)
|
public void AddObserver(IGameStateObserver observer)
|
||||||
{
|
{
|
||||||
observers.Add(observer);
|
observers.Add(observer);
|
||||||
|
observer.Notify(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<Window x:Class="Morris.GameWindow"
|
<Window x:Class="Morris.GameWindow"
|
||||||
|
x:ClassModifier="internal"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
|||||||
@@ -18,7 +18,8 @@ namespace Morris
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Eine WPF-gestütze Mühle-GUI
|
/// Eine WPF-gestütze Mühle-GUI
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class GameWindow : Window, IGameStateObserver, IMoveProvider
|
[SelectorName("GUI"), SingleInstance]
|
||||||
|
internal partial class GameWindow : Window, IGameStateObserver, IMoveProvider
|
||||||
{
|
{
|
||||||
// Diese konstanten Steuern das Aussehen des Spielfelds.
|
// Diese konstanten Steuern das Aussehen des Spielfelds.
|
||||||
private const int BLOCK_SIZE = 100; // Sollte durch 2 teilbar sein
|
private const int BLOCK_SIZE = 100; // Sollte durch 2 teilbar sein
|
||||||
@@ -134,6 +135,8 @@ namespace Morris
|
|||||||
status.Margin = new Thickness(OFFSET_LEFT, STATUS_OFFSET_TOP, 0, 0);
|
status.Margin = new Thickness(OFFSET_LEFT, STATUS_OFFSET_TOP, 0, 0);
|
||||||
status.FontSize = STATUS_SIZE;
|
status.FontSize = STATUS_SIZE;
|
||||||
grid.Children.Add(status);
|
grid.Children.Add(status);
|
||||||
|
|
||||||
|
Show();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Notify(IReadOnlyGameState state)
|
public void Notify(IReadOnlyGameState state)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Morris
|
|||||||
/// Eine Entität, die ein Spiel "abbonieren" kann und dann über Änderungen
|
/// Eine Entität, die ein Spiel "abbonieren" kann und dann über Änderungen
|
||||||
/// des Spielzustands in Kenntnis gesetzt wird
|
/// des Spielzustands in Kenntnis gesetzt wird
|
||||||
/// </summary>
|
/// </summary>
|
||||||
interface IGameStateObserver
|
public interface IGameStateObserver
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Wird aufgerufen, wenn sich der aktuelle Spielzustand geändert hat
|
/// Wird aufgerufen, wenn sich der aktuelle Spielzustand geändert hat
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace Morris
|
|||||||
/// (also in der Regel entweder eine Benutzeroberfläche oder
|
/// (also in der Regel entweder eine Benutzeroberfläche oder
|
||||||
/// ein Bot).
|
/// ein Bot).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
interface IMoveProvider
|
public interface IMoveProvider
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Bestimmt den nächsten Spielzug
|
/// Bestimmt den nächsten Spielzug
|
||||||
|
|||||||
@@ -49,6 +49,9 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="ConsoleInteraction.cs" />
|
<Compile Include="ConsoleInteraction.cs" />
|
||||||
|
<Compile Include="Controller.xaml.cs">
|
||||||
|
<DependentUpon>Controller.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
<Compile Include="CoordinateTranslator.cs" />
|
<Compile Include="CoordinateTranslator.cs" />
|
||||||
<Compile Include="ExtensionMethods.cs" />
|
<Compile Include="ExtensionMethods.cs" />
|
||||||
<Compile Include="Game.cs" />
|
<Compile Include="Game.cs" />
|
||||||
@@ -69,11 +72,18 @@
|
|||||||
<Compile Include="Player.cs" />
|
<Compile Include="Player.cs" />
|
||||||
<Compile Include="Program.cs" />
|
<Compile Include="Program.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
|
<Compile Include="SelectorType.cs" />
|
||||||
|
<Compile Include="SelectorNameAttribute.cs" />
|
||||||
|
<Compile Include="SingleInstanceAttribute.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="App.config" />
|
<None Include="App.config" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Page Include="Controller.xaml">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
<Page Include="GameWindow.xaml">
|
<Page Include="GameWindow.xaml">
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
|||||||
@@ -7,19 +7,20 @@ using System.Threading.Tasks;
|
|||||||
|
|
||||||
namespace Morris
|
namespace Morris
|
||||||
{
|
{
|
||||||
class Program
|
internal class Program
|
||||||
{
|
{
|
||||||
[STAThread]
|
[STAThread]
|
||||||
static void Main(string[] args)
|
static void Main(string[] args)
|
||||||
{
|
{
|
||||||
var a = new ConsoleInteraction();
|
//var a = new ConsoleInteraction();
|
||||||
var b = new RandomBot();
|
//var b = new RandomBot();
|
||||||
var w = new GameWindow();
|
//var w = new GameWindow();
|
||||||
var g = new Game(a, b);
|
//var g = new Game(a, b);
|
||||||
g.AddObserver(a);
|
//g.AddObserver(a);
|
||||||
g.AddObserver(w);
|
//g.AddObserver(w);
|
||||||
Task.Run(() => g.Run(0));
|
//Task.Run(() => g.Run(0));
|
||||||
new Application().Run(w);
|
//new Application().Run(w);
|
||||||
|
new Application().Run(new Controller());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace Morris
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Ein extrem einfacher KI-Spieler, der einen zufälligen gültigen Spielzug auswählt
|
/// Ein extrem einfacher KI-Spieler, der einen zufälligen gültigen Spielzug auswählt
|
||||||
/// </summary>
|
/// </summary>
|
||||||
class RandomBot : IMoveProvider
|
internal class RandomBot : IMoveProvider
|
||||||
{
|
{
|
||||||
// Anhand dieser Klasse können wir sehen, wie einfach es ist, einen Computerspieler zu implementieren.
|
// Anhand dieser Klasse können wir sehen, wie einfach es ist, einen Computerspieler zu implementieren.
|
||||||
// Es muss lediglich eine einzige, einfache Methode implementiert werden. Der Spielzustandparameter stellt
|
// Es muss lediglich eine einzige, einfache Methode implementiert werden. Der Spielzustandparameter stellt
|
||||||
|
|||||||
28
Morris/SelectorNameAttribute.cs
Normal file
28
Morris/SelectorNameAttribute.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
* SelectorNameAttribute.cs
|
||||||
|
* Copyright (c) 2016 Makrus Himmel
|
||||||
|
* This file is distributed un der the terms of the MIT license
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Morris
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Ein Attribut, welches angibt, wie die Klasse im Auswahldialog benannt werden soll
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class, Inherited = true)]
|
||||||
|
public sealed class SelectorNameAttribute : Attribute
|
||||||
|
{
|
||||||
|
public string SelectorName
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SelectorNameAttribute(string selectorName)
|
||||||
|
{
|
||||||
|
SelectorName = selectorName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
43
Morris/SelectorType.cs
Normal file
43
Morris/SelectorType.cs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* SelectorType.cs
|
||||||
|
* Copyright (c) 2016 Markus Himmel
|
||||||
|
* This file is distributed under the terms of the MIT license.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Morris
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Hält einen Typen fest, der potentiell durch ein <see cref="SelectorNameAttribute"/> einen neuen Namen erhalten hat
|
||||||
|
/// </summary>
|
||||||
|
internal class SelectorType
|
||||||
|
{
|
||||||
|
public Type Type
|
||||||
|
{
|
||||||
|
get;
|
||||||
|
private set;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string displayName;
|
||||||
|
|
||||||
|
public SelectorType(Type type)
|
||||||
|
{
|
||||||
|
Type = type;
|
||||||
|
|
||||||
|
// displayName ist SelectorName, falls ein SelectorNameAttribute existiert
|
||||||
|
// und ansonsten einfach der Typname
|
||||||
|
displayName = Type
|
||||||
|
.GetCustomAttribute<SelectorNameAttribute>()
|
||||||
|
?.SelectorName
|
||||||
|
?? type.ToString();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
19
Morris/SingleInstanceAttribute.cs
Normal file
19
Morris/SingleInstanceAttribute.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* SingleInstanceAttribute.cs
|
||||||
|
* Copyright (c) 2016 Markus Himmel
|
||||||
|
* This file ist distributed under the terms of the MIT license
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Morris
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Signalisiert, dass der Controller nur eine Instanz dieser Klasse erstellen sollte,
|
||||||
|
/// wenn sie mehrfach angefordert ist (z.B. nur eine GUI, die Input nimmt und ausgibt)
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Class, Inherited = true)]
|
||||||
|
public sealed class SingleInstanceAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user