Jaki jest najlepszy Pancernik AI?

Battleship!

W 2003 roku (kiedy miałem 17 lat), brałem udział w konkursie kodowania ai Battleship. Mimo że przegrałem ten turniej, świetnie się z niego bawiłem i wiele się z niego nauczyłem.

Teraz chciałbym wskrzesić tę konkurencję, w poszukiwaniu najlepszego pancernika AI.

Oto framework, obecnie hostowany na Bitbucket.

Zwycięzca otrzyma + 450 reputacji! zawody odbędą się w dniu[16]}17. z listopada 2009. Nie będą przyjmowane żadne wpisy ani edycje późniejsze niż godzina zero 17. (Centralny Czas Standardowy) Zgłoś swoje prace wcześniej, aby nie przegapić okazji!

Aby zachować ten obiektywny , postępuj zgodnie z duchem konkursu.

Zasady gry:

  1. gra jest rozgrywana na siatce 10x10.
  2. każdy zawodnik umieści po 5 statków (o długości 2, 3, 3, 4, 5) na ich / align = "left" /
  3. Żadne statki nie mogą się nakładać, ale mogą się przylegać. Zawodnicy na zmianę strzelają pojedynczymi strzałami do przeciwnika.
  • odmiana gry pozwala na oddanie wielu strzałów na salwę, po jednym na każdy ocalały statek.
  • przeciwnik powiadomi rywala, jeśli strzał zatonie, trafi lub nie trafi.
  • Gra kończy się, gdy wszystkie statki jednego gracza zostaną zatopione.

    Regulamin konkurs:

      / Align = "center" bgcolor = "# e0ffe0 " / król Danii / / align = center / Wszystko, co zostanie uznane za niezgodne z duchem konkursu, będzie podstawą do dyskwalifikacji. Ingerowanie w przeciwnika jest sprzeczne z duchem rywalizacji.
    1. wielowątkowość może być stosowana pod następującymi ograniczeniami:
      • nie więcej niż jeden wątek może być uruchomiony, gdy nie jest twoja kolej. (Choć dowolna liczba wątków może być w Stan "zawieszony").
      • żaden wątek nie może działać z priorytetem innym niż"normalny".
      • biorąc pod uwagę powyższe dwa ograniczenia, będziesz mieć zagwarantowane co najmniej 3 dedykowane rdzenie procesora podczas swojej tury.
    2. limit 1 sekundy czasu procesora na grę jest przydzielany każdemu konkurentowi w głównym wątku.
    3. brak czasu powoduje utratę bieżącej gry.
    4. każdy nieobsługiwany wyjątek spowoduje utratę bieżącej gry.
    5. dostęp do sieci dostęp do dysku jest dozwolony, ale ograniczenia czasowe mogą być dość wygórowane. Dodano jednak kilka metod konfiguracji i usuwania, aby złagodzić obciążenie czasowe.
    6. kod powinien być umieszczony na stack overflow jako odpowiedź lub, jeśli jest zbyt duży, linkowany.
    7. maksymalny Całkowity rozmiar (bez kompresji) wpisu to 1 MB.
    8. oficjalnie,. Net 2.0 / 3.5 jest jedynym wymogiem frameworku.
    9. Twój wpis musi zaimplementować IBattleshipOpponent interfejs.

    Punktacja:

    1. najlepsze 51 gier na 101 gier jest zwycięzcą meczu.
    2. wszyscy zawodnicy będą grać dopasowane przeciwko sobie, w stylu round-robin.
    3. najlepsza połowa zawodników rozegra turniej podwójnej eliminacji w celu wyłonienia zwycięzcy. (Najmniejsza z dwóch potęg, która jest większa lub równa połowie, w rzeczywistości.)
    4. będę używać } Turniejapi framework dla turniej.
    5. wyniki zostaną opublikowane tutaj.
    6. jeśli zgłosisz więcej niż jeden wpis, tylko twój najlepiej punktujący wpis jest uprawniony do podwójnego elim.
    Powodzenia! Baw się dobrze!

    Edytuj 1:
    Dzięki Freed , który znalazł błąd w funkcji Ship.IsValid. Zostało naprawione. Pobierz zaktualizowaną wersję frameworka.

    Edytuj 2:
    Ponieważ istnieje duże zainteresowanie utrzymaniem statystyki na dysk i takie tam, dodałem kilka Nie-timed set-up i tear-down zdarzeń, które powinny zapewnić wymaganą funkcjonalność. To jest pół-łamanie zmiany . To znaczy: interfejs został zmodyfikowany, aby dodać funkcje, ale nie jest wymagane ciało dla nich. Pobierz zaktualizowaną wersję frameworka.

    Edytuj 3:
    Poprawka błędu 1: GameWon i GameLost były wywoływane tylko w przypadku przerwy czasowej.
    Poprawka błędu 2: jeśli silnik był wyłączony co gra, rywalizacja nigdy się nie skończy.
    Pobierz zaktualizowaną wersję frameworka.

    Edytuj 4:
    Wyniki Turnieju:

    Author: John Gietzen, 2009-10-27

    25 answers

    Popieram wniosek, aby zrobić o wiele więcej gier na mecz. 50 gier to tylko rzut monetą. Musiałem zrobić 1000 gier, aby uzyskać rozsądne rozróżnienie między algorytmami testowymi.

    Download Dreadnought 1.2 .

    Strategie:

    • Śledź wszystkie możliwe pozycje dla statków, które mają > 0 trafień. Lista nigdy nie staje się większa niż ~30k, więc może być utrzymana dokładnie, w przeciwieństwie do listy wszystkich możliwych pozycji dla wszystkich statków (co jest bardzo duży).

    • Algorytm GetShot składa się z dwóch części. jedna generuje losowe strzały, a druga próbuje zakończyć zatopienie już trafionego statku. Wykonujemy losowe strzały, jeśli istnieje możliwa pozycja (z powyższej listy), w której zatopione zostaną wszystkie trafione statki. W przeciwnym razie staramy się zakończyć zatopienie statku, wybierając miejsce do strzelania, w którym eliminuje się najbardziej możliwe pozycje (ważone).

    • W przypadku losowych ujęć Oblicz najlepszą lokalizację do fotografowania na podstawie / align = "left" /

    • Algorytm adaptacyjny, który umieszcza statki w miejscach, w których przeciwnik statystycznie rzadziej strzela.

    • Algorytm adaptacyjny, który woli strzelać w miejsca, w których przeciwnik statystycznie częściej umieszcza swoje okręty.

    • Umieszczaj statki w większości nie dotykające się nawzajem.

     56
    Author: Keith Randall,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-11-18 18:45:40

    Oto mój wpis! (Najbardziej naiwne rozwiązanie możliwe)

    "Random 1.1"

    namespace Battleship
    {
        using System;
        using System.Collections.ObjectModel;
        using System.Drawing;
    
        public class RandomOpponent : IBattleshipOpponent
        {
            public string Name { get { return "Random"; } }
            public Version Version { get { return this.version; } }
    
            Random rand = new Random();
            Version version = new Version(1, 1);
            Size gameSize;
    
            public void NewGame(Size size, TimeSpan timeSpan)
            {
                this.gameSize = size;
            }
    
            public void PlaceShips(ReadOnlyCollection<Ship> ships)
            {
                foreach (Ship s in ships)
                {
                    s.Place(
                        new Point(
                            rand.Next(this.gameSize.Width),
                            rand.Next(this.gameSize.Height)),
                        (ShipOrientation)rand.Next(2));
                }
            }
    
            public Point GetShot()
            {
                return new Point(
                    rand.Next(this.gameSize.Width),
                    rand.Next(this.gameSize.Height));
            }
    
            public void NewMatch(string opponent) { }
            public void OpponentShot(Point shot) { }
            public void ShotHit(Point shot, bool sunk) { }
            public void ShotMiss(Point shot) { }
            public void GameWon() { }
            public void GameLost() { }
            public void MatchOver() { }
        }
    }
    
     35
    Author: John Gietzen,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-10-30 13:23:29

    Oto przeciwnik dla ludzi do gry:

    Zamiast używać strategii inspirowanej geometrią, pomyślałem, że byłoby interesujące spróbować oszacować podstawowe prawdopodobieństwa że jakaś konkretna niezbadana przestrzeń pomieści statek.

    Aby zrobić to dobrze, zbadasz wszystkie możliwe konfiguracje statków, które pasują do Twojego obecnego widoku świata, a następnie obliczysz prawdopodobieństwo na podstawie tych konfiguracji. Można by pomyśleć o tym jak o odkrywaniu drzewa:

    Rozbudowa możliwych państw pancerników http://natekohl.net/media/battleship-tree.png

    Po rozważeniu wszystkich liści tego drzewa, które łączą się z tym, co wiesz o świecie (np. statki nie mogą się pokrywać, wszystkie trafione kwadraty muszą być statkami itp.) możesz policzyć, jak często statki występują w każdej niezbadanej pozycji, aby oszacować prawdopodobieństwo, że statek tam siedzi.

    To może być wizualizowane jako mapa ciepła, gdzie gorące miejsca są bardziej prawdopodobne, aby zawierać statki:

    Mapa prawdopodobieństwa dla każdej niezbadanej pozycji http://natekohl.net/media/battleship-probs.png

    Jedna rzecz, którą lubię w tej konkurencji Battleship jest to, że drzewo powyżej jest prawie wystarczająco małe, aby brutalnie wymusić tego rodzaju algorytm. Jeśli istnieje ~150 możliwych pozycji dla każdego z 5 statków, to 1505 = 75 miliard możliwości. I to Liczba tylko się zmniejsza, zwłaszcza jeśli można wyeliminować całe statki.

    Przeciwnik, z którym linkowałem powyżej, nie odkrywa całego drzewa; 75 miliardów jest jeszcze za duże, aby dostać się w ciągu sekundy. Próbuje jednak oszacować te prawdopodobieństwa za pomocą kilku heurystyk.

     22
    Author: Nate Kohl,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-11-13 02:58:46

    Nie jest to w pełni rozwinięta odpowiedź, ale wydaje się, że nie ma sensu zaśmiecać prawdziwych odpowiedzi kodem, który jest powszechny. Przedstawiam więc niektóre rozszerzenia / klasy ogólne w duchu open source. Jeśli ich używasz, Zmień przestrzeń nazw lub spróbuj skompilować wszystko w jeden dll nie będzie działać.

    BoardView umożliwia łatwą pracę z tablicą z komentarzami.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Drawing;
    using System.IO;
    
    namespace Battleship.ShuggyCoUk
    {
        public enum Compass
        {
            North,East,South,West
        }
    
        class Cell<T>
        {
            private readonly BoardView<T> view;
            public readonly int X;
            public readonly int Y;
            public T Data;
            public double Bias { get; set; }
    
            public Cell(BoardView<T> view, int x, int y) 
            { 
                this.view = view; this.X = x; this.Y = y; this.Bias = 1.0;  
            }
    
            public Point Location
            {
                get { return new Point(X, Y); }
            }
    
            public IEnumerable<U> FoldAll<U>(U acc, Func<Cell<T>, U, U> trip)
            {
                return new[] { Compass.North, Compass.East, Compass.South, Compass.West }
                    .Select(x => FoldLine(x, acc, trip));
            }
    
            public U FoldLine<U>(Compass direction, U acc, Func<Cell<T>, U, U> trip)
            {
                var cell = this;
                while (true)
                {
                    switch (direction)
                    {
                        case Compass.North:
                            cell = cell.North; break;
                        case Compass.East:
                            cell = cell.East; break;
                        case Compass.South:
                            cell = cell.South; break;
                        case Compass.West:
                            cell = cell.West; break;
                    }
                    if (cell == null)
                        return acc;
                    acc = trip(cell, acc);
                }
            }
    
            public Cell<T> North
            {
                get { return view.SafeLookup(X, Y - 1); }
            }
    
            public Cell<T> South
            {
                get { return view.SafeLookup(X, Y + 1); }
            }
    
            public Cell<T> East
            {
                get { return view.SafeLookup(X+1, Y); }
            }
    
            public Cell<T> West
            {
                get { return view.SafeLookup(X-1, Y); }
            }
    
            public IEnumerable<Cell<T>> Neighbours()
            {
                if (North != null)
                    yield return North;
                if (South != null)
                    yield return South;
                if (East != null)
                    yield return East;
                if (West != null)
                    yield return West;
            }
        }
    
        class BoardView<T>  : IEnumerable<Cell<T>>
        {
            public readonly Size Size;
            private readonly int Columns;
            private readonly int Rows;
    
            private Cell<T>[] history;
    
            public BoardView(Size size)
            {
                this.Size = size;
                Columns = size.Width;
                Rows = size.Height;
                this.history = new Cell<T>[Columns * Rows];
                for (int y = 0; y < Rows; y++)
                {
                    for (int x = 0; x < Rows; x++)
                        history[x + y * Columns] = new Cell<T>(this, x, y);
                }
            }
    
            public T this[int x, int y]
            {
                get { return history[x + y * Columns].Data; }
                set { history[x + y * Columns].Data = value; }
            }
    
            public T this[Point p]
            {
                get { return history[SafeCalc(p.X, p.Y, true)].Data; }
                set { this.history[SafeCalc(p.X, p.Y, true)].Data = value; }
            }
    
            private int SafeCalc(int x, int y, bool throwIfIllegal)
            {
                if (x < 0 || y < 0 || x >= Columns || y >= Rows)
                {    if (throwIfIllegal)
                        throw new ArgumentOutOfRangeException("["+x+","+y+"]");
                     else
                        return -1;
                }
                return x + y * Columns;
            }
    
            public void Set(T data)
            {
                foreach (var cell in this.history)
                    cell.Data = data;
            }
    
            public Cell<T> SafeLookup(int x, int y)
            {
                int index = SafeCalc(x, y, false);
                if (index < 0)
                    return null;
                return history[index];
            }
    
            #region IEnumerable<Cell<T>> Members
    
            public IEnumerator<Cell<T>> GetEnumerator()
            {
                foreach (var cell in this.history)
                    yield return cell;
            }
    
            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return this.GetEnumerator();
            }
    
            public BoardView<U> Transform<U>(Func<T, U> transform)
            {
                var result = new BoardView<U>(new Size(Columns, Rows));
                for (int y = 0; y < Rows; y++)
                {
                    for (int x = 0; x < Columns; x++)
                    {
                        result[x,y] = transform(this[x, y]);
                    }
                }
                return result;
            }
    
            public void WriteAsGrid(TextWriter w)
            {
                WriteAsGrid(w, "{0}");
            }
    
            public void WriteAsGrid(TextWriter w, string format)
            {
                WriteAsGrid(w, x => string.Format(format, x.Data));
            }
    
            public void WriteAsGrid(TextWriter w, Func<Cell<T>,string> perCell)
            {
                for (int y = 0; y < Rows; y++)
                {
                    for (int x = 0; x < Columns; x++)
                    {
                        if (x != 0)
                            w.Write(",");
                        w.Write(perCell(this.SafeLookup(x, y)));
                    }
                    w.WriteLine();
                }
            }
    
            #endregion
        }
    }
    

    Niektóre rozszerzenia, niektóre z tych duplikatów funkcjonalności w głównym frameworku, ale naprawdę należy to zrobić przez Ciebie.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Drawing;
    using System.Collections.ObjectModel;
    
    namespace Battleship.ShuggyCoUk
    {
        public static class Extensions
        {        
            public static bool IsIn(this Point p, Size size)
            {
                return p.X >= 0 && p.Y >= 0 && p.X < size.Width && p.Y < size.Height;
            }
    
            public static bool IsLegal(this Ship ship,
                IEnumerable<Ship> ships, 
                Size board,
                Point location, 
                ShipOrientation direction)
            {
                var temp = new Ship(ship.Length);
                temp.Place(location, direction);
                if (!temp.GetAllLocations().All(p => p.IsIn(board)))
                    return false;
                return ships.Where(s => s.IsPlaced).All(s => !s.ConflictsWith(temp));
            }
    
            public static bool IsTouching(this Point a, Point b)
            {
                return (a.X == b.X - 1 || a.X == b.X + 1) &&
                    (a.Y == b.Y - 1 || a.Y == b.Y + 1);
            }
    
            public static bool IsTouching(this Ship ship,
                IEnumerable<Ship> ships,
                Point location,
                ShipOrientation direction)
            {
                var temp = new Ship(ship.Length);
                temp.Place(location, direction);
                var occupied = new HashSet<Point>(ships
                    .Where(s => s.IsPlaced)
                    .SelectMany(s => s.GetAllLocations()));
                if (temp.GetAllLocations().Any(p => occupied.Any(b => b.IsTouching(p))))
                    return true;
                return false;
            }
    
            public static ReadOnlyCollection<Ship> MakeShips(params int[] lengths)
            {
                return new System.Collections.ObjectModel.ReadOnlyCollection<Ship>(
                    lengths.Select(l => new Ship(l)).ToList());       
            }
    
            public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Rand rand)
            {
                T[] elements = source.ToArray();
                // Note i > 0 to avoid final pointless iteration
                for (int i = elements.Length - 1; i > 0; i--)
                {
                    // Swap element "i" with a random earlier element it (or itself)
                    int swapIndex = rand.Next(i + 1);
                    T tmp = elements[i];
                    elements[i] = elements[swapIndex];
                    elements[swapIndex] = tmp;
                }
                // Lazily yield (avoiding aliasing issues etc)
                foreach (T element in elements)
                {
                    yield return element;
                }
            }
    
            public static T RandomOrDefault<T>(this IEnumerable<T> things, Rand rand)
            {
                int count = things.Count();
                if (count == 0)
                    return default(T);
                return things.ElementAt(rand.Next(count));
            }
        }
    }
    
    Coś, czego często używam.
    enum OpponentsBoardState
    {
        Unknown = 0,
        Miss,
        MustBeEmpty,        
        Hit,
    }
    

    Randomizacja. Bezpieczny, ale testowalny, przydatny do testowania.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Drawing;
    
    namespace Battleship.ShuggyCoUk
    {
        public class Rand
        {
            Random r;
    
            public Rand()
            {
                var rand = System.Security.Cryptography.RandomNumberGenerator.Create();
                byte[] b = new byte[4];
                rand.GetBytes(b);
                r = new Random(BitConverter.ToInt32(b, 0));
            }
    
            public int Next(int maxValue)
            {
                return r.Next(maxValue);
            }
    
            public double NextDouble(double maxValue)
            {
                return r.NextDouble() * maxValue;
            }
    
            public T Pick<T>(IEnumerable<T> things)
            {
                return things.ElementAt(Next(things.Count()));
            }
    
            public T PickBias<T>(Func<T, double> bias, IEnumerable<T> things)
            {
                double d = NextDouble(things.Sum(x => bias(x)));
                foreach (var x in things)
                {
                    if (d < bias(x))
                        return x;
                    d -= bias(x);                
                }
                throw new InvalidOperationException("fell off the end!");
            }
        }
    }
    
     12
    Author: ShuggyCoUk,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-11-16 01:08:53

    Nie mam teraz czasu na napisanie pełnowartościowego algorytmu, ale oto myśl: jeśli twój przeciwnik umieszczał statki losowo, czy prawdopodobieństwo umieszczenia nie byłoby prostym rozkładem skoncentrowanym na (5,5,5,5)? Na przykład, możliwości umieszczenia pancernika (5 jednostek długości) w wymiarze x są tutaj:

    x    1 2 3 4 5  6  7 8 9 10
    P(x) 2 4 6 8 10 10 8 6 4 2
    

    Te same obliczenia byłyby poprawne dla y. pozostałe statki nie miałyby tak stromych rozkładów, ale twoje najlepsze przypuszczenie jest nadal centrum. Potem, matematyczne podejście byłoby powolne promieniowanie przekątnych (być może z długością przeciętnego statku, 17/5) z centrum. Ex:

    ...........
    ....x.x....
    .....x.....
    ....x.x....
    ...........
    

    Oczywiście jakaś przypadkowość musi być dodana do tego pomysłu, ale myślę, że czysto matematycznie to jest droga do zrobienia.

     10
    Author: ine,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-10-30 20:42:04

    Nic tak wyszukanego poza tym, co wymyśliłem. Pokonuje losowego przeciwnika 99,9% czasu. Byłby zainteresowany, gdyby ktoś miał jakieś inne małe wyzwania, takie jak to, to była dobra zabawa.

    namespace Battleship
    {
        using System;
        using System.Collections.ObjectModel;
        using System.Drawing;
        using System.Collections.Generic;
        using System.Linq;
        public class AgentSmith : IBattleshipOpponent
        {        
            public string Name { get { return "Agent Smith"; } }
            public Version Version { get { return this.version; } }
            private Random rand = new Random();
            private Version version = new Version(2, 1);
            private Size gameSize;
            private enum Direction { Up, Down, Left, Right }
            private int MissCount;
            private Point?[] EndPoints = new Point?[2];
            private LinkedList<Point> HitShots = new LinkedList<Point>();
            private LinkedList<Point> Shots = new LinkedList<Point>();
            private List<Point> PatternShots = new List<Point>();
            private Direction ShotDirection = Direction.Up;
            private void NullOutTarget()
            {
                EndPoints = new Point?[2];
                MissCount = 0;
            }
            private void SetupPattern()
            {
                for (int y = 0; y < gameSize.Height; y++)
                    for (int x = 0; x < gameSize.Width; x++)
                        if ((x + y) % 2 == 0) PatternShots.Add(new Point(x, y));
            }
            private bool InvalidShot(Point p)
            {
                bool InvalidShot = (Shots.Where(s => s.X == p.X && s.Y == p.Y).Any());
                if (p.X < 0 | p.Y<0) InvalidShot = true;
                if (p.X >= gameSize.Width | p.Y >= gameSize.Height) InvalidShot = true;
                return InvalidShot;
            }
            private Point FireDirectedShot(Direction? direction, Point p)
            {
                ShotDirection = (Direction)direction;
                switch (ShotDirection)
                {
                    case Direction.Up: p.Y--; break;
                    case Direction.Down: p.Y++; break;
                    case Direction.Left: p.X--; break;
                    case Direction.Right: p.X++; break;
                }
                return p;
            }
            private Point FireAroundPoint(Point p)
            {
                if (!InvalidShot(FireDirectedShot(ShotDirection,p)))
                    return FireDirectedShot(ShotDirection, p);
                Point testShot = FireDirectedShot(Direction.Left, p);
                if (InvalidShot(testShot)) { testShot = FireDirectedShot(Direction.Right, p); }
                if (InvalidShot(testShot)) { testShot = FireDirectedShot(Direction.Up, p); }
                if (InvalidShot(testShot)) { testShot = FireDirectedShot(Direction.Down, p); }
                return testShot;
            }
            private Point FireRandomShot()
            {
                Point p;
                do
                {
                    if (PatternShots.Count > 0)
                        PatternShots.Remove(p = PatternShots[rand.Next(PatternShots.Count)]);
                    else do
                        {
                            p = FireAroundPoint(HitShots.First());
                            if (InvalidShot(p)) HitShots.RemoveFirst();
                        } while (InvalidShot(p) & HitShots.Count > 0);
                }
                while (InvalidShot(p));
                return p;
            }
            private Point FireTargettedShot()
            {
                Point p;
                do
                {
                    p = FireAroundPoint(new Point(EndPoints[1].Value.X, EndPoints[1].Value.Y));
                    if (InvalidShot(p) & EndPoints[1] != EndPoints[0])
                        EndPoints[1] = EndPoints[0];
                    else if (InvalidShot(p)) NullOutTarget();
                } while (InvalidShot(p) & EndPoints[1] != null);
                if (InvalidShot(p)) p = FireRandomShot();
                return p;
            }
            private void ResetVars()
            {
                Shots.Clear();
                HitShots.Clear();
                PatternShots.Clear();
                MissCount = 0;
            }
            public void NewGame(Size size, TimeSpan timeSpan)
            {
                gameSize = size;
                ResetVars();
                SetupPattern();
            }
            public void PlaceShips(ReadOnlyCollection<Ship> ships)
            {
                foreach (Ship s in ships)
                    s.Place(new Point(rand.Next(this.gameSize.Width), rand.Next(this.gameSize.Height)), (ShipOrientation)rand.Next(2));
            }
            public Point GetShot()
            {
                if (EndPoints[1] != null) Shots.AddLast(FireTargettedShot());
                else Shots.AddLast(FireRandomShot());
                return Shots.Last();
            }
            public void ShotHit(Point shot, bool sunk)
            {            
                HitShots.AddLast(shot);
                MissCount = 0;
                EndPoints[1] = shot;
                if (EndPoints[0] == null) EndPoints[0] = shot;
                if (sunk) NullOutTarget();
            }
            public void ShotMiss(Point shot)
            {
                if (++MissCount == 6) NullOutTarget();
            }
            public void GameWon() { }
            public void GameLost() { }
            public void NewMatch(string opponent) { }
            public void OpponentShot(Point shot) { }
            public void MatchOver() { }
        }
    }
    

    Lekko skondensowany, aby zająć minimalną ilość miejsca i nadal być czytelny.

     10
    Author: Gavin,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-11-06 14:46:22

    Kilka uwag o silniku konkurencji:

    Parametry NewGame:

    Jeśli IBattleshipOpponent:: NewGame jest przeznaczony do konfiguracji przed grą i ma rozmiar planszy, powinien również wziąć listę statków i ich odpowiednie rozmiary. Nie ma sensu zezwalać na zmienny rozmiar płyty bez zezwalania na zmienne konfiguracje statku.

    Statki są zamknięte:

    Nie widzę powodu, dla którego statek klasy jest zapieczętowany. Między innymi, chciałbym Statki mieć nazwę, więc mogę wysyłać wiadomości typu ("zatopiłeś moje {0}", ship.Name); . mam też na myśli inne rozszerzenia, więc myślę, że statek powinien być dziedziczny.

    Terminy:

    Podczas gdy limit czasu 1 sekundy ma sens dla reguły turnieju, to całkowicie miesza z debugowaniem. BattleshipCompetition powinno mieć łatwe ustawienie ignorowania naruszeń czasu, aby pomóc w rozwoju/debugowaniu. Sugerowałbym również zbadanie System.Diagnostyka.Process:: UserProcessorTime / Privileged ProcessorTime / TotalProcessorTime dla dokładniejszego widoku, ile czasu jest używane.

    Zatopione Statki:

    Obecne API informuje o zatopieniu statku oppenenta:

    ShotHit(Point shot, bool sunk);
    

    Ale nie który statek zatonąłeś! Uważam to za część zasad walki ludzi z pancernikami, które musisz zadeklarować: "zatopiłeś mój Pancernik!"(lub niszczyciela, lub okrętu podwodnego itp.

    Jest to szczególnie ważne, gdy sztuczna inteligencja próbuje wypłoszyć statki, które walczą ze sobą. Chciałbym poprosić o zmianę API na:
    ShotHit(Point shot, Ship ship);
    

    Jeśli statek jest nie-null, oznacza to, że strzał był Tonący-shot, I wiesz, który statek zatonął, i jak długo to trwało. Jeśli strzał nie był Tonący, to statek jest zerowy i nie masz więcej informacji.

     6
    Author: abelenky,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-10-30 22:49:57

    CrossFire zaktualizowany. Wiem, że nie może konkurować z Farnsworthem lub Dreadnought, ale jest o wiele szybszy niż ten drugi i prosty do zabawy w razie, gdyby ktoś chciał spróbować. Zależy to od aktualnego stanu moich bibliotek,dołączonych tutaj, aby ułatwić korzystanie z nich.

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Drawing;
    using System.IO;
    using System.Collections.ObjectModel;
    
    namespace Battleship.ShuggyCoUk
    {
        public class Simple : IBattleshipOpponent
        {
            BoardView<OpponentsBoardState> opponentsBoard = new BoardView<OpponentsBoardState>(new Size(10,10));
            Rand rand = new Rand();
            int gridOddEven;
            Size size;
    
            public string Name { get { return "Simple"; } }
    
            public Version Version { get { return new Version(2, 1); }}
    
            public void NewMatch(string opponent) {}
    
            public void NewGame(System.Drawing.Size size, TimeSpan timeSpan)
            {
                this.size = size;
                this.opponentsBoard = new BoardView<OpponentsBoardState>(size);
                this.gridOddEven = rand.Pick(new[] { 0, 1 });
            }
    
            public void PlaceShips(System.Collections.ObjectModel.ReadOnlyCollection<Ship> ships)
            {
                BoardView<bool> board = new BoardView<bool>(size);
                var AllOrientations = new[] {
                    ShipOrientation.Horizontal,
                    ShipOrientation.Vertical };
    
                foreach (var ship in ships)
                {
                    int avoidTouching = 3;
                    while (!ship.IsPlaced)
                    {
                        var l = rand.Pick(board.Select(c => c.Location));
                        var o = rand.Pick(AllOrientations);
                        if (ship.IsLegal(ships, size, l, o))
                        {
                            if (ship.IsTouching(ships, l, o)&& --avoidTouching > 0)
                                continue;
                            ship.Place(l, o);
                        }
                    }
                }
            }
            protected virtual Point PickWhenNoTargets()
            {
                return rand.PickBias(x => x.Bias,
                    opponentsBoard
                    // nothing 1 in size
                    .Where(c => (c.Location.X + c.Location.Y) % 2 == gridOddEven)
                    .Where(c => c.Data == OpponentsBoardState.Unknown))
                    .Location;
            }
    
            private int SumLine(Cell<OpponentsBoardState> c, int acc)
            {
                if (acc >= 0)
                    return acc;
                if (c.Data == OpponentsBoardState.Hit)
                    return acc - 1;
                return -acc;
            }
    
            public System.Drawing.Point GetShot()
            {
                var targets = opponentsBoard
                    .Where(c => c.Data == OpponentsBoardState.Hit)
                    .SelectMany(c => c.Neighbours())
                    .Where(c => c.Data == OpponentsBoardState.Unknown)
                    .ToList();
                if (targets.Count > 1)
                {
                    var lines = targets.Where(
                        x => x.FoldAll(-1, SumLine).Select(r => Math.Abs(r) - 1).Max() > 1).ToList();
                    if (lines.Count > 0)
                        targets = lines;
                }
                var target = targets.RandomOrDefault(rand);
                if (target == null)
                    return PickWhenNoTargets();
                return target.Location;
            }
    
            public void OpponentShot(System.Drawing.Point shot)
            {
            }
    
            public void ShotHit(Point shot, bool sunk)
            {
                opponentsBoard[shot] = OpponentsBoardState.Hit;
                Debug(shot, sunk);
            }
    
            public void ShotMiss(Point shot)
            {
                opponentsBoard[shot] = OpponentsBoardState.Miss;
                Debug(shot, false);
            }
    
            public const bool DebugEnabled = false;
    
            public void Debug(Point shot, bool sunk)
            {
                if (!DebugEnabled)
                    return;
                opponentsBoard.WriteAsGrid(
                    Console.Out,
                    x =>
                    {
                        string t;
                        switch (x.Data)
                        {
                            case OpponentsBoardState.Unknown:
                                return " ";
                            case OpponentsBoardState.Miss:
                                t = "m";
                                break;
                            case OpponentsBoardState.MustBeEmpty:
                                t = "/";
                                break;
                            case OpponentsBoardState.Hit:
                                t = "x";
                                break;
                            default:
                                t = "?";
                                break;
                        }
                        if (x.Location == shot)
                            t = t.ToUpper();
                        return t;
                    });
                if (sunk)
                    Console.WriteLine("sunk!");
                Console.ReadLine();
            }
    
            public void GameWon()
            {
            }
    
            public void GameLost()
            {
            }
    
            public void MatchOver()
            {
            }
    
            #region Library code
            enum OpponentsBoardState
            {
                Unknown = 0,
                Miss,
                MustBeEmpty,
                Hit,
            }
    
            public enum Compass
            {
                North, East, South, West
            }
    
            class Cell<T>
            {
                private readonly BoardView<T> view;
                public readonly int X;
                public readonly int Y;
                public T Data;
                public double Bias { get; set; }
    
                public Cell(BoardView<T> view, int x, int y)
                {
                    this.view = view; this.X = x; this.Y = y; this.Bias = 1.0;
                }
    
                public Point Location
                {
                    get { return new Point(X, Y); }
                }
    
                public IEnumerable<U> FoldAll<U>(U acc, Func<Cell<T>, U, U> trip)
                {
                    return new[] { Compass.North, Compass.East, Compass.South, Compass.West }
                        .Select(x => FoldLine(x, acc, trip));
                }
    
                public U FoldLine<U>(Compass direction, U acc, Func<Cell<T>, U, U> trip)
                {
                    var cell = this;
                    while (true)
                    {
                        switch (direction)
                        {
                            case Compass.North:
                                cell = cell.North; break;
                            case Compass.East:
                                cell = cell.East; break;
                            case Compass.South:
                                cell = cell.South; break;
                            case Compass.West:
                                cell = cell.West; break;
                        }
                        if (cell == null)
                            return acc;
                        acc = trip(cell, acc);
                    }
                }
    
                public Cell<T> North
                {
                    get { return view.SafeLookup(X, Y - 1); }
                }
    
                public Cell<T> South
                {
                    get { return view.SafeLookup(X, Y + 1); }
                }
    
                public Cell<T> East
                {
                    get { return view.SafeLookup(X + 1, Y); }
                }
    
                public Cell<T> West
                {
                    get { return view.SafeLookup(X - 1, Y); }
                }
    
                public IEnumerable<Cell<T>> Neighbours()
                {
                    if (North != null)
                        yield return North;
                    if (South != null)
                        yield return South;
                    if (East != null)
                        yield return East;
                    if (West != null)
                        yield return West;
                }
            }
    
            class BoardView<T> : IEnumerable<Cell<T>>
            {
                public readonly Size Size;
                private readonly int Columns;
                private readonly int Rows;
    
                private Cell<T>[] history;
    
                public BoardView(Size size)
                {
                    this.Size = size;
                    Columns = size.Width;
                    Rows = size.Height;
                    this.history = new Cell<T>[Columns * Rows];
                    for (int y = 0; y < Rows; y++)
                    {
                        for (int x = 0; x < Rows; x++)
                            history[x + y * Columns] = new Cell<T>(this, x, y);
                    }
                }
    
                public T this[int x, int y]
                {
                    get { return history[x + y * Columns].Data; }
                    set { history[x + y * Columns].Data = value; }
                }
    
                public T this[Point p]
                {
                    get { return history[SafeCalc(p.X, p.Y, true)].Data; }
                    set { this.history[SafeCalc(p.X, p.Y, true)].Data = value; }
                }
    
                private int SafeCalc(int x, int y, bool throwIfIllegal)
                {
                    if (x < 0 || y < 0 || x >= Columns || y >= Rows)
                    {
                        if (throwIfIllegal)
                            throw new ArgumentOutOfRangeException("[" + x + "," + y + "]");
                        else
                            return -1;
                    }
                    return x + y * Columns;
                }
    
                public void Set(T data)
                {
                    foreach (var cell in this.history)
                        cell.Data = data;
                }
    
                public Cell<T> SafeLookup(int x, int y)
                {
                    int index = SafeCalc(x, y, false);
                    if (index < 0)
                        return null;
                    return history[index];
                }
    
                #region IEnumerable<Cell<T>> Members
    
                public IEnumerator<Cell<T>> GetEnumerator()
                {
                    foreach (var cell in this.history)
                        yield return cell;
                }
    
                System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
                {
                    return this.GetEnumerator();
                }
    
                public BoardView<U> Transform<U>(Func<T, U> transform)
                {
                    var result = new BoardView<U>(new Size(Columns, Rows));
                    for (int y = 0; y < Rows; y++)
                    {
                        for (int x = 0; x < Columns; x++)
                        {
                            result[x, y] = transform(this[x, y]);
                        }
                    }
                    return result;
                }
    
                public void WriteAsGrid(TextWriter w)
                {
                    WriteAsGrid(w, "{0}");
                }
    
                public void WriteAsGrid(TextWriter w, string format)
                {
                    WriteAsGrid(w, x => string.Format(format, x.Data));
                }
    
                public void WriteAsGrid(TextWriter w, Func<Cell<T>, string> perCell)
                {
                    for (int y = 0; y < Rows; y++)
                    {
                        for (int x = 0; x < Columns; x++)
                        {
                            if (x != 0)
                                w.Write(",");
                            w.Write(perCell(this.SafeLookup(x, y)));
                        }
                        w.WriteLine();
                    }
                }
    
                #endregion
            }
    
            public class Rand
            {
                Random r;
    
                public Rand()
                {
                    var rand = System.Security.Cryptography.RandomNumberGenerator.Create();
                    byte[] b = new byte[4];
                    rand.GetBytes(b);
                    r = new Random(BitConverter.ToInt32(b, 0));
                }
    
                public int Next(int maxValue)
                {
                    return r.Next(maxValue);
                }
    
                public double NextDouble(double maxValue)
                {
                    return r.NextDouble() * maxValue;
                }
    
                public T Pick<T>(IEnumerable<T> things)
                {
                    return things.ElementAt(Next(things.Count()));
                }
    
                public T PickBias<T>(Func<T, double> bias, IEnumerable<T> things)
                {
                    double d = NextDouble(things.Sum(x => bias(x)));
                    foreach (var x in things)
                    {
                        if (d < bias(x))
                            return x;
                        d -= bias(x);
                    }
                    throw new InvalidOperationException("fell off the end!");
                }
            }
            #endregion
        }
    
        public static class Extensions
        {
            public static bool IsIn(this Point p, Size size)
            {
                return p.X >= 0 && p.Y >= 0 && p.X < size.Width && p.Y < size.Height;
            }
    
            public static bool IsLegal(this Ship ship,
                IEnumerable<Ship> ships,
                Size board,
                Point location,
                ShipOrientation direction)
            {
                var temp = new Ship(ship.Length);
                temp.Place(location, direction);
                if (!temp.GetAllLocations().All(p => p.IsIn(board)))
                    return false;
                return ships.Where(s => s.IsPlaced).All(s => !s.ConflictsWith(temp));
            }
    
            public static bool IsTouching(this Point a, Point b)
            {
                return (a.X == b.X - 1 || a.X == b.X + 1) &&
                    (a.Y == b.Y - 1 || a.Y == b.Y + 1);
            }
    
            public static bool IsTouching(this Ship ship,
                IEnumerable<Ship> ships,
                Point location,
                ShipOrientation direction)
            {
                var temp = new Ship(ship.Length);
                temp.Place(location, direction);
                var occupied = new HashSet<Point>(ships
                    .Where(s => s.IsPlaced)
                    .SelectMany(s => s.GetAllLocations()));
                if (temp.GetAllLocations().Any(p => occupied.Any(b => b.IsTouching(p))))
                    return true;
                return false;
            }
    
            public static ReadOnlyCollection<Ship> MakeShips(params int[] lengths)
            {
                return new System.Collections.ObjectModel.ReadOnlyCollection<Ship>(
                    lengths.Select(l => new Ship(l)).ToList());
            }
    
            public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Battleship.ShuggyCoUk.Simple.Rand rand)
            {
                T[] elements = source.ToArray();
                // Note i > 0 to avoid final pointless iteration
                for (int i = elements.Length - 1; i > 0; i--)
                {
                    // Swap element "i" with a random earlier element it (or itself)
                    int swapIndex = rand.Next(i + 1);
                    T tmp = elements[i];
                    elements[i] = elements[swapIndex];
                    elements[swapIndex] = tmp;
                }
                // Lazily yield (avoiding aliasing issues etc)
                foreach (T element in elements)
                {
                    yield return element;
                }
            }
    
            public static T RandomOrDefault<T>(this IEnumerable<T> things, Battleship.ShuggyCoUk.Simple.Rand rand)
            {
                int count = things.Count();
                if (count == 0)
                    return default(T);
                return things.ElementAt(rand.Next(count));
            }
        }
    

    }

     5
    Author: ShuggyCoUk,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-11-16 10:42:45

    To jest najlepsze, co mogłem zebrać w wolnym czasie, czyli o nieistniejącym. Dzieje się trochę statystyk dotyczących gry i meczu, ponieważ ustawiłem główną funkcję, aby zapętlić i stale uruchamiać Konkurs BattleshipCompetition, dopóki nie nacisnąłem klawisza.

    namespace Battleship
    {
        using System;
        using System.Collections.Generic;
        using System.Drawing;
        using System.Linq;
    
        public class BP7 : IBattleshipOpponent
        {
            public string Name { get { return "BP7"; } }
            public Version Version { get { return this.version; } }
    
            Random rand = new Random();
            Version version = new Version(0, 7);
            Size gameSize;
            List<Point> scanShots;
            List<NextShot> nextShots;
            int wins, losses;
            int totalWins = 0;
            int totalLosses = 0;
            int maxWins = 0;
            int maxLosses = 0;
            int matchWins = 0;
            int matchLosses = 0;
    
            public enum Direction { VERTICAL = -1, UNKNOWN = 0, HORIZONTAL = 1 };
            Direction hitDirection, lastShotDirection;
    
            enum ShotResult { UNKNOWN, MISS, HIT };
            ShotResult[,] board;
    
            public struct NextShot
            {
                public Point point;
                public Direction direction;
                public NextShot(Point p, Direction d)
                {
                    point = p;
                    direction = d;
                }
            }
    
            public struct ScanShot
            {
                public Point point;
                public int openSpaces;
                public ScanShot(Point p, int o)
                {
                    point = p;
                    openSpaces = o;
                }
            }
    
            public void NewGame(Size size, TimeSpan timeSpan)
            {
                this.gameSize = size;
                scanShots = new List<Point>();
                nextShots = new List<NextShot>();
                fillScanShots();
                hitDirection = Direction.UNKNOWN;
                board = new ShotResult[size.Width, size.Height];
            }
    
            private void fillScanShots()
            {
                int x;
                for (x = 0; x < gameSize.Width - 1; x++)
                {
                    scanShots.Add(new Point(x, x));
                }
    
                if (gameSize.Width == 10)
                {
                    for (x = 0; x < 3; x++)
                    {
                        scanShots.Add(new Point(9 - x, x));
                        scanShots.Add(new Point(x, 9 - x));
                    }
                }
            }
    
            public void PlaceShips(System.Collections.ObjectModel.ReadOnlyCollection<Ship> ships)
            {
                foreach (Ship s in ships)
                {
                    s.Place(
                        new Point(
                            rand.Next(this.gameSize.Width),
                            rand.Next(this.gameSize.Height)),
                        (ShipOrientation)rand.Next(2));
                }
            }
    
            public Point GetShot()
            {
                Point shot;
    
                if (this.nextShots.Count > 0)
                {
                    if (hitDirection != Direction.UNKNOWN)
                    {
                        if (hitDirection == Direction.HORIZONTAL)
                        {
                            this.nextShots = this.nextShots.OrderByDescending(x => x.direction).ToList();
                        }
                        else
                        {
                            this.nextShots = this.nextShots.OrderBy(x => x.direction).ToList();
                        }
                    }
    
                    shot = this.nextShots.First().point;
                    lastShotDirection = this.nextShots.First().direction;
                    this.nextShots.RemoveAt(0);
                    return shot;
                }
    
                List<ScanShot> scanShots = new List<ScanShot>();
                for (int x = 0; x < gameSize.Width; x++)
                {
                    for (int y = 0; y < gameSize.Height; y++)
                    {
                        if (board[x, y] == ShotResult.UNKNOWN)
                        {
                            scanShots.Add(new ScanShot(new Point(x, y), OpenSpaces(x, y)));
                        }
                    }
                }
                scanShots = scanShots.OrderByDescending(x => x.openSpaces).ToList();
                int maxOpenSpaces = scanShots.FirstOrDefault().openSpaces;
    
                List<ScanShot> scanShots2 = new List<ScanShot>();
                scanShots2 = scanShots.Where(x => x.openSpaces == maxOpenSpaces).ToList();
                shot = scanShots2[rand.Next(scanShots2.Count())].point;
    
                return shot;
            }
    
            int OpenSpaces(int x, int y)
            {
                int ctr = 0;
                Point p;
    
                // spaces to the left
                p = new Point(x - 1, y);
                while (p.X >= 0 && board[p.X, p.Y] == ShotResult.UNKNOWN)
                {
                    ctr++;
                    p.X--;
                }
    
                // spaces to the right
                p = new Point(x + 1, y);
                while (p.X < gameSize.Width && board[p.X, p.Y] == ShotResult.UNKNOWN)
                {
                    ctr++;
                    p.X++;
                }
    
                // spaces to the top
                p = new Point(x, y - 1);
                while (p.Y >= 0 && board[p.X, p.Y] == ShotResult.UNKNOWN)
                {
                    ctr++;
                    p.Y--;
                }
    
                // spaces to the bottom
                p = new Point(x, y + 1);
                while (p.Y < gameSize.Height && board[p.X, p.Y] == ShotResult.UNKNOWN)
                {
                    ctr++;
                    p.Y++;
                }
    
                return ctr;
            }
    
            public void NewMatch(string opponenet)
            {
                wins = 0;
                losses = 0;
            }
    
            public void OpponentShot(Point shot) { }
    
            public void ShotHit(Point shot, bool sunk)
            {
                board[shot.X, shot.Y] = ShotResult.HIT;
    
                if (!sunk)
                {
                    hitDirection = lastShotDirection;
                    if (shot.X != 0)
                    {
                        this.nextShots.Add(new NextShot(new Point(shot.X - 1, shot.Y), Direction.HORIZONTAL));
                    }
    
                    if (shot.Y != 0)
                    {
                        this.nextShots.Add(new NextShot(new Point(shot.X, shot.Y - 1), Direction.VERTICAL));
                    }
    
                    if (shot.X != this.gameSize.Width - 1)
                    {
                        this.nextShots.Add(new NextShot(new Point(shot.X + 1, shot.Y), Direction.HORIZONTAL));
                    }
    
                    if (shot.Y != this.gameSize.Height - 1)
                    {
                        this.nextShots.Add(new NextShot(new Point(shot.X, shot.Y + 1), Direction.VERTICAL));
                    }
                }
                else
                {
                    hitDirection = Direction.UNKNOWN;
                    this.nextShots.Clear();     // so now this works like gangbusters ?!?!?!?!?!?!?!?!?
                }
            }
    
            public void ShotMiss(Point shot)
            {
                board[shot.X, shot.Y] = ShotResult.MISS;
            }
    
            public void GameWon()
            {
                wins++;
            }
    
            public void GameLost()
            {
                losses++;
            }
    
            public void MatchOver()
            {
                if (wins > maxWins)
                {
                    maxWins = wins;
                }
    
                if (losses > maxLosses)
                {
                    maxLosses = losses;
                }
    
                totalWins += wins;
                totalLosses += losses;
    
                if (wins >= 51)
                {
                    matchWins++;
                }
                else
                {
                    matchLosses++;
                }
            }
    
            public void FinalStats()
            {
                Console.WriteLine("Games won: " + totalWins.ToString());
                Console.WriteLine("Games lost: " + totalLosses.ToString());
                Console.WriteLine("Game winning percentage: " + (totalWins * 1.0 / (totalWins + totalLosses)).ToString("P"));
                Console.WriteLine("Game losing percentage: " + (totalLosses * 1.0 / (totalWins + totalLosses)).ToString("P"));
                Console.WriteLine();
                Console.WriteLine("Matches won: " + matchWins.ToString());
                Console.WriteLine("Matches lost: " + matchLosses.ToString());
                Console.WriteLine("Match winning percentage: " + (matchWins * 1.0 / (matchWins + matchLosses)).ToString("P"));
                Console.WriteLine("Match losing percentage: " + (matchLosses * 1.0 / (matchWins + matchLosses)).ToString("P"));
                Console.WriteLine("Match games won high: " + maxWins.ToString());
                Console.WriteLine("Match games lost high: " + maxLosses.ToString());
                Console.WriteLine();
            }
        }
    }
    
    Ta logika jest najbliższa do pokonania Dreadnought, wygrywając około 41% poszczególnych gier. (W rzeczywistości wygrał jeden mecz zliczając 52 do 49.) Co dziwne, ta klasa nie radzi sobie tak dobrze z FarnsworthOpponent jako wcześniejsza wersja, która była znacznie mniej zaawansowana.
     5
    Author: BP.,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-11-17 15:06:33

    Mój komputer jest obecnie naprawiany przez della, ale to tutaj byłem w zeszłym tygodniu:

    namespace Battleship
    {
        using System;
        using System.Collections.ObjectModel;
        using System.Drawing;
        using System.Collections.Generic;
        using System.Linq;
    
        public class BSKiller4 : OpponentExtended, IBattleshipOpponent
        {
            public string Name { get { return "BSKiller4"; } }
            public Version Version { get { return this.version; } }
    
            public bool showBoard = false;
    
            Random rand = new Random();
            Version version = new Version(0, 4);
            Size gameSize;
    
            List<Point> nextShots;
            Queue<Point> scanShots;
    
            char[,] board;
    
            private void printBoard()
            {
                Console.WriteLine();
                for (int y = 0; y < this.gameSize.Height; y++)
                {
                    for (int x = 0; x < this.gameSize.Width; x++)
                    {
                        Console.Write(this.board[x, y]);
                    }
                    Console.WriteLine();
                }
                Console.ReadKey();
            }
    
            public void NewGame(Size size, TimeSpan timeSpan)
            {
                this.gameSize = size;
                board = new char[size.Width, size.Height];
                this.nextShots = new List<Point>();
                this.scanShots = new Queue<Point>();
                fillScanShots();
                initializeBoard();
            }
    
            private void initializeBoard()
            {
                for (int y = 0; y < this.gameSize.Height; y++)
                {
                    for (int x = 0; x < this.gameSize.Width; x++)
                    {
                        this.board[x, y] = 'O';
                    }
                }
            }
    
            private void fillScanShots()
            {
                int x, y;
                int num = gameSize.Width * gameSize.Height;
                for (int j = 0; j < 3; j++)
                {
                    for (int i = j; i < num; i += 3)
                    {
                        x = i % gameSize.Width;
                        y = i / gameSize.Height;
                        scanShots.Enqueue(new Point(x, y));
                    }
                }
            }
    
            public void PlaceShips(ReadOnlyCollection<Ship> ships)
            {
                foreach (Ship s in ships)
                {
                    s.Place(new Point(
                            rand.Next(this.gameSize.Width),
                            rand.Next(this.gameSize.Height)),
                            (ShipOrientation)rand.Next(2));
                }
            }
    
            public Point GetShot()
            {
                if (showBoard) printBoard();
                Point shot;
    
                shot = findShotRun();
                if (shot.X != -1)
                {
                    return shot;
                }
    
                if (this.nextShots.Count > 0)
                {
                    shot = this.nextShots[0];
                    this.nextShots.RemoveAt(0);
                }
                else
                {
                    shot = this.scanShots.Dequeue();
                }
    
                return shot;
            }
    
            public void ShotHit(Point shot, bool sunk)
            {
                this.board[shot.X, shot.Y] = 'H';
                if (!sunk)
                {
                    addToNextShots(new Point(shot.X - 1, shot.Y));
                    addToNextShots(new Point(shot.X, shot.Y + 1));
                    addToNextShots(new Point(shot.X + 1, shot.Y));
                    addToNextShots(new Point(shot.X, shot.Y - 1));
                }
                else
                {
                    this.nextShots.Clear();
                }
            }
    
    
    
            private Point findShotRun()
            {
                int run_forward_horizontal = 0;
                int run_backward_horizontal = 0;
                int run_forward_vertical = 0;
                int run_backward_vertical = 0;
    
                List<shotPossibilities> possible = new List<shotPossibilities>(5);
    
                // this only works if width = height for the board;
                for (int y = 0; y < this.gameSize.Height; y++)
                {
                    for (int x = 0; x < this.gameSize.Width; x++)
                    {
                        // forward horiz
                        if (this.board[x, y] == 'M')
                        {
                            run_forward_horizontal = 0;
                        }
                        else if (this.board[x, y] == 'O')
                        {
                            if (run_forward_horizontal >= 2)
                            {
                                possible.Add(
                                    new shotPossibilities(
                                        run_forward_horizontal,
                                        new Point(x, y),
                                        true));
                            }
                            else
                            {
                                run_forward_horizontal = 0;
                            }
                        }
                        else
                        {
                            run_forward_horizontal++;
                        }
    
                        // forward vertical
                        if (this.board[y, x] == 'M')
                        {
                            run_forward_vertical = 0;
                        }
                        else if (this.board[y, x] == 'O')
                        {
                            if (run_forward_vertical >= 2)
                            {
                                possible.Add(
                                    new shotPossibilities(
                                        run_forward_vertical,
                                        new Point(y, x),
                                        false));
                            }
                            else
                            {
                                run_forward_vertical = 0;
                            }
                        }
                        else
                        {
                            run_forward_vertical++;
                        }
    
    
                        // backward horiz
                        if (this.board[this.gameSize.Width - x - 1, y] == 'M')
                        {
                            run_backward_horizontal = 0;
                        }
                        else if (this.board[this.gameSize.Width - x - 1, y] == 'O')
                        {
                            if (run_backward_horizontal >= 2)
                            {
                                possible.Add(
                                    new shotPossibilities(
                                        run_backward_horizontal,
                                        new Point(this.gameSize.Width - x - 1, y),
                                        true));
                            }
                            else
                            {
                                run_backward_horizontal = 0;
                            }
                        }
                        else
                        {
                            run_backward_horizontal++;
                        }
    
    
                        // backward vertical
                        if (this.board[y, this.gameSize.Height - x - 1] == 'M')
                        {
                            run_backward_vertical = 0;
                        }
                        else if (this.board[y, this.gameSize.Height - x - 1] == 'O')
                        {
                            if (run_backward_vertical >= 2)
                            {
                                possible.Add(
                                    new shotPossibilities(
                                        run_backward_vertical,
                                        new Point(y, this.gameSize.Height - x - 1),
                                        false));
                            }
                            else
                            {
                                run_backward_vertical = 0;
                            }
                        }
                        else
                        {
                            run_backward_vertical++;
                        }
    
                    }
    
                    run_forward_horizontal = 0;
                    run_backward_horizontal = 0;
                    run_forward_vertical = 0;
                    run_backward_vertical = 0;
                }
                Point shot;
    
                if (possible.Count > 0)
                {
                    shotPossibilities shotp = possible.OrderByDescending(a => a.run).First();
                    //this.nextShots.Clear();
                    shot = shotp.shot;
                    //if (shotp.isHorizontal)
                    //{
                    //    this.nextShots.RemoveAll(p => p.X != shot.X);
                    //}
                    //else
                    //{
                    //    this.nextShots.RemoveAll(p => p.Y != shot.Y);
                    //}
                }
                else
                {
                    shot = new Point(-1, -1);
                }
    
                return shot;
            }
    
            private void addToNextShots(Point p)
            {
                if (!this.nextShots.Contains(p) &&
                    p.X >= 0 &&
                    p.X < this.gameSize.Width &&
                    p.Y >= 0 &&
                    p.Y < this.gameSize.Height)
                {
                    if (this.board[p.X, p.Y] == 'O')
                    {
                        this.nextShots.Add(p);
                    }
                }
            }
    
            public void GameWon()
            {
                this.GameWins++;
            }
    
            public void NewMatch(string opponent)
            {
                System.Threading.Thread.Sleep(5);
                this.rand = new Random(System.Environment.TickCount);
            }
            public void OpponentShot(Point shot) { }
            public void ShotMiss(Point shot)
            {
                this.board[shot.X, shot.Y] = 'M';
            }
            public void GameLost()
            {
                if (showBoard) Console.WriteLine("-----Game Over-----");
            }
            public void MatchOver() { }
        }
    
    
        public class OpponentExtended
        {
            public int GameWins { get; set; }
            public int MatchWins { get; set; }
            public OpponentExtended() { }
        }
    
        public class shotPossibilities
        {
            public shotPossibilities(int r, Point s, bool h)
            {
                this.run = r;
                this.shot = s;
                this.isHorizontal = h;
            }
            public int run { get; set; }
            public Point shot { get; set; }
            public bool isHorizontal { get; set; }
        }
    }
    
     5
    Author: John Boker,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-11-17 22:17:23

    Jeśli brutalnie wymuszasz swoją analizę, możesz uznać, że mechanika dostarczonego Randomopponenta jest wysoce nieefektywna. Pozwala sobie na ponowne wybranie już docelowych lokalizacji i pozwala framework zmusić go do powtarzania, dopóki nie trafi w jeden, którego jeszcze nie dotknął lub termin na ruch wygaśnie.

    Ten przeciwnik ma podobne zachowanie (efektywny rozkład lokacji jest taki sam) po prostu sam sprawdza rozsądek i zużywa tylko jedną losowo generowaną liczbę na wywołanie (amortyzowane)).

    To używa klas z moich rozszerzeń / odpowiedzi biblioteki i podaję tylko kluczowe metody / stan.

    Shuffle jest zniesiony z Jon Skeet odpowiedzi tutaj

    class WellBehavedRandomOpponent : IBattleShipOpponent
    {
        Rand rand = new Rand();
        List<Point> guesses;
        int nextGuess = 0;
    
        public void PlaceShips(IEnumerable<Ship> ships)
        {
            BoardView<bool> board = new BoardView<bool>(BoardSize);
            var AllOrientations = new[] {
                ShipOrientation.Horizontal,
                ShipOrientation.Vertical };
    
            foreach (var ship in ships)
            {
                while (!ship.IsPlaced)
                {
                    var l = rand.Pick(board.Select(c => c.Location));
                    var o = rand.Pick(AllOrientations);
                    if (ship.IsLegal(ships, BoardSize, l, o))
                        ship.Place(l, o);
                }
            }
        }
    
        public void NewGame(Size size, TimeSpan timeSpan)
        {
            var board = new BoardView<bool>(size);
            this.guesses = new List<Point>(
                board.Select(x => x.Location).Shuffle(rand));
            nextGuess = 0;
        }
    
        public System.Drawing.Point GetShot()
        {
            return guesses[nextGuess++];
        }
    
        // empty methods left out 
    }
    
     4
    Author: ShuggyCoUk,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2017-05-23 12:02:38

    Nie będę mógł uczestniczyć, ale oto algorytm, który zaimplementowałbym gdybym miał czas:

    Po pierwsze, kiedy wykryję trafienie, nie ścigam od razu reszty statku - buduję tabelę lokalizacji statków i zastanawiam się, czy przynajmniej raz trafiłem wszystkie pięć, zanim zacznę je całkowicie zatopić. (Zauważ, że jest to zła polityka dla wariantu Wielokrotnego Strzału-Zobacz komentarze)

    1. uderz w środek (patrz końcowa uwaga poniżej - 'center' to tylko wygoda dla opis)
    2. Hit the spot 4 to the right of the center
    3. uderz w punkt 1 w dół i jeden na prawo od centrum
    4. Hit the spot four to the right of the previous hit
    5. Kontynuuj zgodnie z tym wzorem (powinny skończyć się ukośnymi liniami oddzielonymi 3 spacjami wypełniającymi planszę) to powinno trafić wszystkie łodzie o długości 4 i 5 oraz statystycznie dużą liczbę 3 i 2 łodzi.

    6. Zacznij losowo trafiając miejsca inbetween przekątnych, to złapać 2 i 3 długości łodzi, które nie zostały jeszcze zauważone.

    Gdy wykryję 5 trafień, ustalę, czy 5 trafień jest na osobnych łodziach. Jest to stosunkowo łatwe, wykonując kilka strzałów w pobliżu miejsc, w których dwa trafienia znajdują się na tej samej poziomej lub pionowej linii i znajdują się w 5 miejscach od siebie (mogą to być dwa trafienia na tej samej łodzi). Jeśli są to oddzielne łodzie, to nadal tonąć wszystkie statki. Jeśli okaże się, że jest to ta sama łódź, Kontynuuj wzory napełniania powyżej, aż wszystkie 5 łodzi znajdują się.

    Algorytm ten jest prostym algorytmem wypełniania. Najważniejsze cechy to to, że nie marnuje czasu na zatapianie statków, o których wie, gdy wciąż są statki, o których nie wie, i nie używa nieefektywnego schematu napełniania (tj. w pełni losowy wzór byłby marnotrawny).

    Uwagi końcowe:

    A)" Center " jest przypadkowym punktem wyjścia na planszy. Eliminuje to podstawową słabość tego algorytmu. B) podczas gdy Opis wskazuje rysowanie przekątnych od razu od początku, najlepiej algorytm tylko strzela w "przypadkowych" miejscach, które są wzdłuż tych przekątnych. Pomaga to uniemożliwić konkurentowi odmierzenie czasu, kiedy ich statki zostaną trafione przez przewidywalne wzorce.

    Opisuje to' idealny ' algorytm w tym, że dostanie wszystkie statki poniżej (9x9) / 2+10 strzałów.

    Można go jednak znacznie poprawić:

    Gdy statek zostanie trafiony, określ jego rozmiar przed wykonaniem "wewnętrzne" linie ukośne. Być może znalazłeś statek 2, w którym to przypadku wewnętrzne przekątne można uprościć, aby szybciej znaleźć statki o rozmiarze 3.

    Określ etapy w grze i działaj zgodnie z nimi. Algorytm ten może być dobry do pewnego momentu w grze, ale inne algorytmy mogą przynieść lepsze korzyści w ramach gry końcowej. Ponadto, jeśli drugi gracz jest bardzo bliski pokonania ciebie, inny algorytm może działać lepiej - na przykład algorytm wysokiego ryzyka może zawieść bardziej często, ale kiedy to działa to działa szybko i można pokonać przeciwnika, który jest bliżej wygranej niż ty.

    Określ styl gry konkurenta - może to dać ci wskazówki, jak planują rozmieszczenie statków (tj. są duże szanse, że ich własny algorytm najszybciej zidentyfikuje, jak umieszczają własne statki - jeśli jedynym narzędziem, które masz, jest młotek, wszystko wygląda jak gwóźdź) {]}

    - Adam

     4
    Author: Adam Davis,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-11-12 16:57:57

    Mój wpis.

    Nic specjalnego, i nie miałem czasu, aby dodać wszystkie dobre pomysły, które miałem.

    Ale wydaje się grać dość dobrze. Zobaczymy jak będzie w konkurencji:

    (Umieść to w pliku Missouri.cs i dodaj do projektu.)

    using System;
    using System.Collections.ObjectModel;
    using System.Drawing;
    using System.Collections.Generic;
    using System.Linq;
    using System.Diagnostics;
    
    namespace Battleship
    {
        // The Empire of Japan surrendered on the deck of the USS Missouri on Sept. 2, 1945
        public class USSMissouri : IBattleshipOpponent
        {
            public String  Name    { get { return name; } }
            public Version Version { get { return ver;  } }
    
    #region IBattleship Interface
            // IBattleship::NewGame
            public void NewGame(Size gameSize, TimeSpan timeSpan)
            {
                size      = gameSize;
                shotBoard = new ShotBoard(size);
                attackVector = new Stack<Attack>();
            }
    
            // IBattleship::PlaceShips
            public void PlaceShips(ReadOnlyCollection<Ship> ships)
            {
                HunterBoard board;
                targetBoards = new List<HunterBoard>();
                shotBoard    = new ShotBoard(size);
                foreach (Ship s in ships)
                {
                    board = new HunterBoard(this, size, s);
                    targetBoards.Add(board);
    
                    // REWRITE: to ensure valid board placement.
                    s.Place(
                        new Point(
                            rand.Next(size.Width),
                            rand.Next(size.Height)),
                        (ShipOrientation)rand.Next(2));
                }
            }
    
            // IBattleship::GetShot
            public Point GetShot()
            {
                Point p = new Point();
    
                if (attackVector.Count() > 0)
                {
                    p = ExtendShot();
                    return p;
                }
    
                // Contemplate a shot at every-single point, and measure how effective it would be.
                Board potential = new Board(size);
                for(p.Y=0; p.Y<size.Height; ++p.Y)
                {
                    for(p.X=0; p.X<size.Width; ++p.X)
                    {
                        if (shotBoard.ShotAt(p))
                        {
                            potential[p] = 0;
                            continue;
                        }
    
                        foreach(HunterBoard b in targetBoards)
                        {
                            potential[p] += b.GetWeightAt(p);
                        }
                    }
                }
    
                // Okay, we have the shot potential of the board.
                // Lets pick a weighted-random spot.
                Point shot;
                shot = potential.GetWeightedRandom(rand.NextDouble());
    
                shotBoard[shot] = Shot.Unresolved;
    
                return shot;
            }
    
            public Point ExtendShot()
            {
                // Lets consider North, South, East, and West of the current shot.
                // and measure the potential of each
                Attack attack = attackVector.Peek();
    
                Board potential = new Board(size);
    
                Point[] points = attack.GetNextTargets();
                foreach(Point p in points)
                {
                    if (shotBoard.ShotAt(p))
                    {
                        potential[p] = 0;
                        continue;
                    }
    
                    foreach(HunterBoard b in targetBoards)
                    {
                        potential[p] += b.GetWeightAt(p);
                    }
                }
    
                Point shot = potential.GetBestShot();
                shotBoard[shot] = Shot.Unresolved;
                return shot;
            }
    
            // IBattleship::NewMatch
            public void NewMatch(string opponent)
            {
            }
            public void OpponentShot(Point shot)
            {
            }
            public void ShotHit(Point shot, bool sunk)
            {
                shotBoard[shot] = Shot.Hit;
    
                if (!sunk)
                {
                    if (attackVector.Count == 0) // This is a first hit, open an attackVector
                    {   
                        attackVector.Push(new Attack(this, shot));
                    }
                    else
                    {
                        attackVector.Peek().AddHit(shot);    // Add a hit to our current attack.
                    }
                }
    
                // What if it is sunk?  Close the top attack, which we've been pursuing.
                if (sunk)
                {
                    if (attackVector.Count > 0)
                    {
                        attackVector.Pop();
                    }
                }
            }
            public void ShotMiss(Point shot)
            {
                shotBoard[shot] = Shot.Miss;
    
                foreach(HunterBoard b in targetBoards)
                {
                    b.ShotMiss(shot);  // Update the potential map.
                }
            }
            public void GameWon()
            {
                Trace.WriteLine  ("I won the game!");
            }
            public void GameLost()
            {
                Trace.WriteLine  ("I lost the game!");
            }
            public void MatchOver()
            {
                Trace.WriteLine("This match is over.");
            }
    
    #endregion 
    
            public ShotBoard theShotBoard
            {
                get { return shotBoard; }
            }
            public Size theBoardSize
            {
                get { return size; }
            }
    
            private Random rand = new Random();
            private Version ver = new Version(6, 3); // USS Missouri is BB-63, hence version 6.3
            private String name = "USS Missouri ([email protected])";
            private Size size;
            private List<HunterBoard> targetBoards;
            private ShotBoard shotBoard;
            private Stack<Attack> attackVector;
        }
    
        // An Attack is the data on the ship we are currently working on sinking.
        // It consists of a set of points, horizontal and vertical, from a central point.
        // And can be extended in any direction.
        public class Attack
        {
            public Attack(USSMissouri root, Point p)
            {
                Player = root;
                hit = p;
                horzExtent = new Extent(p.X, p.X);
                vertExtent = new Extent(p.Y, p.Y);
            }
    
            public Extent HorizontalExtent
            {
                get { return horzExtent; }
            }
            public Extent VerticalExtent
            {
                get { return vertExtent; }
            }
            public Point  FirstHit
            {
                get { return hit; }
            }
    
            public void AddHit(Point p)
            {
                if (hit.X == p.X) // New hit in the vertical direction
                {
                    vertExtent.Min = Math.Min(vertExtent.Min, p.Y);
                    vertExtent.Max = Math.Max(vertExtent.Max, p.Y);
                }
                else if (hit.Y == p.Y)
                {
                    horzExtent.Min = Math.Min(horzExtent.Min, p.X);
                    horzExtent.Max = Math.Max(horzExtent.Max, p.X);
                }
            }
            public Point[] GetNextTargets() 
            {
                List<Point> bors = new List<Point>();
    
                Point p;
    
                p = new Point(hit.X, vertExtent.Min-1);
                while (p.Y >= 0 && Player.theShotBoard[p] == Shot.Hit)
                {
                    if (Player.theShotBoard[p] == Shot.Miss)
                    {
                        break; // Don't add p to the List 'bors.  
                    }
                    --p.Y;
                }
                if (p.Y >= 0 && Player.theShotBoard[p] == Shot.None) // Add next-target only if there is no shot here yet.
                {
                    bors.Add(p);
                }
    
                //-------------------
    
                p = new Point(hit.X, vertExtent.Max+1);
                while (p.Y < Player.theBoardSize.Height && Player.theShotBoard[p] == Shot.Hit)
                {
                    if (Player.theShotBoard[p] == Shot.Miss)
                    {
                        break; // Don't add p to the List 'bors.  
                    }
                    ++p.Y;
                }
                if (p.Y < Player.theBoardSize.Height && Player.theShotBoard[p] == Shot.None)
                {
                    bors.Add(p);
                }
    
                //-------------------
    
                p = new Point(horzExtent.Min-1, hit.Y);
                while (p.X >= 0 && Player.theShotBoard[p] == Shot.Hit)
                {
                    if (Player.theShotBoard[p] == Shot.Miss)
                    {
                        break; // Don't add p to the List 'bors.  
                    }
                    --p.X;
                }
                if (p.X >= 0 && Player.theShotBoard[p] == Shot.None)
                {
                    bors.Add(p);
                }
    
                //-------------------
    
                p = new Point(horzExtent.Max+1, hit.Y);
                while (p.X < Player.theBoardSize.Width && Player.theShotBoard[p] == Shot.Hit)
                {
                    if (Player.theShotBoard[p] == Shot.Miss)
                    {
                        break; // Don't add p to the List 'bors.  
                    }
                    ++p.X;
                }
                if (p.X < Player.theBoardSize.Width && Player.theShotBoard[p] == Shot.None)
                {
                    bors.Add(p);
                }
    
                return bors.ToArray();
            }
    
            private Point hit; 
            private Extent horzExtent;
            private Extent vertExtent;
            private USSMissouri Player;
        }
    
        public struct Extent
        {
            public Extent(Int32 min, Int32 max)
            {
                Min = min;
                Max = max;
            }
            public Int32 Min;
            public Int32 Max;
        }
    
        public class Board  // The potential-Board, which measures the full potential of each square.
        {
            // A Board is the status of many things.
            public Board(Size boardsize)
            {
                size = boardsize;
                grid = new int[size.Width , size.Height];
                Array.Clear(grid,0,size.Width*size.Height);
            }
    
            public int this[int c,int r]
            {
                get { return grid[c,r];  }
                set { grid[c,r] = value; }
            }
            public int this[Point p]
            {
                get { return grid[p.X, p.Y];  }
                set { grid[p.X, p.Y] = value; }
            }
    
            public Point GetWeightedRandom(double r)
            {
                Int32 sum = 0;
                foreach(Int32 i in grid)
                {
                    sum += i;
                }
    
                Int32 index = (Int32)(r*sum);
    
                Int32 x=0, y=0;
                for(y=0; y<size.Height; ++y)
                {
                    for(x=0; x<size.Width; ++x)
                    {
                        if (grid[x,y] == 0) continue; // Skip any zero-cells
                        index -= grid[x,y];
                        if (index < 0) break;
                    }
                    if (index < 0) break;
                }
    
                if (x == 10 || y == 10)
                    throw new Exception("WTF");
    
                return new Point(x,y);
            }
    
            public Point GetBestShot()
            {
                int max=grid[0,0];
                for(int y=0; y<size.Height; ++y)
                {
                    for (int x=0; x<size.Width; ++x)
                    {
                        max = (grid[x,y] > max)? grid[x,y] : max;
                    }
                }
    
                for(int y=0; y<size.Height; ++y)
                {
                    for (int x=0; x<size.Width; ++x)
                    {
                        if (grid[x,y] == max)
                        {
                            return new Point(x,y);
                        }
                    }
                }
                return new Point(0,0);
            }
    
            public bool IsZero()
            {
                foreach(Int32 p in grid)
                {
                    if (p > 0)
                    {
                        return false;
                    }
                }
                return true;
            }
    
            public override String ToString()
            {
                String output = "";
                String horzDiv = "   +----+----+----+----+----+----+----+----+----+----+\n";
                String disp;
                int x,y;
    
                output += "      A    B    C    D    E    F    G    H    I    J    \n" + horzDiv;
    
                for(y=0; y<size.Height; ++y)
                {
                    output += String.Format("{0} ", y+1).PadLeft(3);
                    for(x=0; x<size.Width; ++x)
                    {
                        switch(grid[x,y])
                        {
                            case (int)Shot.None:       disp = "";  break;
                            case (int)Shot.Hit:        disp = "#"; break;
                            case (int)Shot.Miss:       disp = "."; break;
                            case (int)Shot.Unresolved: disp = "?"; break;
                            default:                   disp = "!"; break;
                        }
    
                        output += String.Format("| {0} ", disp.PadLeft(2));
                    }
                    output += "|\n" + horzDiv;
                }
    
                return output;
            }
    
            protected Int32[,] grid;
            protected Size     size;
        }
    
        public class HunterBoard
        {
            public HunterBoard(USSMissouri root, Size boardsize, Ship target)
            {
                size = boardsize;
                grid = new int[size.Width , size.Height];
                Array.Clear(grid,0,size.Width*size.Height);
    
                Player = root;
                Target = target;
                Initialize();
            }
    
            public void Initialize()
            {
                int x, y, i;
    
                for(y=0; y<size.Height; ++y)
                {
                    for(x=0; x<size.Width - Target.Length+1; ++x)
                    {
                        for(i=0; i<Target.Length; ++i)
                        {
                            grid[x+i,y]++;
                        }
                    }
                }
    
                for(y=0; y<size.Height-Target.Length+1; ++y)
                {
                    for(x=0; x<size.Width; ++x)
                    {
                        for(i=0; i<Target.Length; ++i)
                        {
                            grid[x,y+i]++;
                        }
                    }
                }
            }
    
            public int this[int c,int r]
            {
                get { return grid[c,r];  }
                set { grid[c,r] = value; }
            }
            public int this[Point p]
            {
                get { return grid[p.X, p.Y];  }
                set { grid[p.X, p.Y] = value; }
            }
    
            public void ShotMiss(Point p)
            {
                int x,y;
                int min, max;
    
                min = Math.Max(p.X-Target.Length+1, 0);
                max = Math.Min(p.X, size.Width-Target.Length);
                for(x=min; x<=max; ++x)
                {
                    DecrementRow(p.Y, x, x+Target.Length-1);
                }
    
                min = Math.Max(p.Y-Target.Length+1, 0);
                max = Math.Min(p.Y, size.Height-Target.Length);
                for(y=min; y<=max; ++y)
                {
                    DecrementColumn(p.X, y, y+Target.Length-1);
                } 
    
                grid[p.X, p.Y] = 0;
            }
    
            public void ShotHit(Point p)
            {
            }
    
            public override String ToString()
            {
                String output = String.Format("Target size is {0}\n", Target.Length);
                String horzDiv = "   +----+----+----+----+----+----+----+----+----+----+\n";
                int x,y;
    
                output += "      A    B    C    D    E    F    G    H    I    J    \n" + horzDiv;
                for(y=0; y<size.Height; ++y)
                {
                    output += String.Format("{0} ", y+1).PadLeft(3);
                    for(x=0; x<size.Width; ++x)
                    {
                        output += String.Format("| {0} ", grid[x,y].ToString().PadLeft(2));
                    }
                    output += "|\n" + horzDiv;
                }
                return output;
            }
    
            // If we shoot at point P, how does that affect the potential of the board?
            public Int32 GetWeightAt(Point p)
            {
                int x,y;
                int potential = 0;
                int min, max;
    
                min = Math.Max(p.X-Target.Length+1, 0);
                max = Math.Min(p.X, size.Width-Target.Length);
                for(x=min; x<=max; ++x)
                {
                    if (Player.theShotBoard.isMissInRow(p.Y, x, x+Target.Length-1) == false)
                    {
                        ++potential;
                    }
                }
    
                min = Math.Max(p.Y-Target.Length+1, 0);
                max = Math.Min(p.Y, size.Height-Target.Length);
                for(y=min; y<=max; ++y)
                {
                    if (Player.theShotBoard.isMissInColumn(p.X, y, y+Target.Length-1) == false)
                    {
                        ++potential;
                    }
                } 
    
                return potential;
            }
    
            public void DecrementRow(int row, int rangeA, int rangeB)
            {
                int x;
                for(x=rangeA; x<=rangeB; ++x)
                {
                    grid[x,row] = (grid[x,row]==0)? 0 : grid[x,row]-1;
                }
            }
            public void DecrementColumn(int col, int rangeA, int rangeB)
            {
                int y;
                for(y=rangeA; y<=rangeB; ++y)
                {
                    grid[col,y] = (grid[col,y]==0)? 0 : grid[col,y]-1;
                }
            }
    
            private Ship Target = null;
            private USSMissouri Player;
            private Int32[,] grid;
            private Size     size;
        }
    
        public enum Shot
        {
            None = 0,
            Hit = 1,
            Miss = 2,
            Unresolved = 3
        };
    
        public class ShotBoard
        {
            public ShotBoard(Size boardsize)
            {
                size = boardsize;
                grid = new Shot[size.Width , size.Height];
    
                for(int y=0; y<size.Height; ++y)
                {
                    for(int x=0; x<size.Width; ++x)
                    {
                        grid[x,y] = Shot.None;
                    }
                }
            }
    
            public Shot this[int c,int r]
            {
                get { return grid[c,r];  }
                set { grid[c,r] = value; }
            }
            public Shot this[Point p]
            {
                get { return grid[p.X, p.Y];  }
                set { grid[p.X, p.Y] = value; }
            }
    
            public override String ToString()
            {
                String output = "";
                String horzDiv = "   +----+----+----+----+----+----+----+----+----+----+\n";
                String disp;
                int x,y;
    
                output += "      A    B    C    D    E    F    G    H    I    J    \n" + horzDiv;
    
                for(y=0; y<size.Height; ++y)
                {
                    output += String.Format("{0} ", y+1).PadLeft(3);
                    for(x=0; x<size.Width; ++x)
                    {
                        switch(grid[x,y])
                        {
                            case Shot.None:       disp = "";  break;
                            case Shot.Hit:        disp = "#"; break;
                            case Shot.Miss:       disp = "."; break;
                            case Shot.Unresolved: disp = "?"; break;
                            default:              disp = "!"; break;
                        }
    
                        output += String.Format("| {0} ", disp.PadLeft(2));
                    }
                    output += "|\n" + horzDiv;
                }
                return output;
            }
    
            // Functions to find shots on the board, at a specific point, or in a row or column, within a range
            public bool ShotAt(Point p)
            {
                return !(this[p]==Shot.None);
            }
            public bool isMissInColumn(int col, int rangeA, int rangeB)
            {
                for(int y=rangeA; y<=rangeB; ++y)
                {
                    if (grid[col,y] == Shot.Miss)
                    {
                        return true;
                    }
                }
                return false;
            }
            public bool isMissInRow(int row, int rangeA, int rangeB)
            {
                for(int x=rangeA; x<=rangeB; ++x)
                {
                    if (grid[x,row] == Shot.Miss)
                    {
                        return true;
                    }
                }
                return false;
            }
            protected Shot[,] grid;
            protected Size     size;
        }
    }
    
     4
    Author: abelenky,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-11-17 22:30:33

    To nie jest minimax. Właściwie po umieszczeniu statków, czy każdy gracz nie może grać na własną rękę, co spowodowało, że wiele obrotów zajęło mu zatopienie każdego statku przeciwnika? Wygrywa ten, który miał mniej zakrętów.

    Myślę, że nie ma żadnych dobrych ogólnych strategii poza toneniem trafionych statków i próbami zminimalizowania liczby strzałów, aby pokryć Pozostałe możliwe miejsca, w których statki mogłyby się ukryć.

    Oczywiście mogą istnieć kontrargumenty na wszystko, co nie jest przypadkowe. Ale Ja nie myśl, że istnieją strategie, które są dobre wobec wszystkich możliwych graczy.

     2
    Author: ziggystar,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-10-27 16:36:59

    Właściwie, myślę, że największym problemem z puzzle jest to, że jego zasadniczo dwa ruchy. Jeden ruch polega na umieszczeniu statków, drugi na znalezieniu wrogich statków (jednak druga część może być, oprócz próby pobicia zegara z czynnikiem losowym, po prostu "uruchom algorytm"). Nie ma mechanizmu, aby próbować określić, a następnie przeciwdziałać strategii wroga, co sprawia, że podobne konkursy oparte na kolejnych rundach "rock paper scissors" są dość interesujące.

    Ponadto, myślę, że byłoby fajniej, gdybyś określił grę jako protokół sieciowy, a następnie dostarczył framework do implementacji tego Protokołu w C#, zamiast dyktować, że wszystkie rozwiązania powinny być C#, ale to tylko moje zdanie.

    EDIT: wycofuję swój wstępny punkt, ponieważ nie przeczytałem wystarczająco uważnie regulaminu konkursu.

     2
    Author: Jherico,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-10-27 17:06:15

    Zawsze lubiłem zaczynać od środka i oddalać się od tego jednego punktu, pozostawiając nie więcej niż 1 puste miejsce między innymi punktami, aby wyjaśnić tę cholerną łódź podwodną... odstęp między strzałami był uzależniony od tego, które okręty zostały zatopione. jeśli B-ship był ostatni, strzały musiały pozostawić tylko 4 miejsca pomiędzy, aby zminimalizować zmarnowane strzały

     2
    Author: CheeseConQueso,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-10-27 17:06:46

    Podobny konkurs prowadził Dr James Heather z Uniwersytetu Surrey w imieniu British Computer Society.

    Ograniczono zasoby-mianowicie maksymalny czas procesora na turę, nie można było zapisać żadnego stanu między ruchami, nałożono maksymalny rozmiar sterty. Aby ograniczyć czas, sztuczna inteligencja może przesłać ruch w dowolnym momencie w przedziale czasowym i zostanie poproszona o ruch po zakończeniu tury.

    Bardzo ciekawe-Zobacz więcej na: http://www.bcsstudentcontest.com/

    Może da ci więcej pomysłów.

     2
    Author: Tom Duckering,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-10-27 17:41:10

    Jak to jest, rozwiązanie otwiera się i działa bez modyfikacji w monodevelop w ubuntu 9.10 linux

     2
    Author: John Boker,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-10-31 04:30:38

    Napisałeś:

      Wszystko, co zostanie uznane za niezgodne z duchem konkursu, będzie podstawą do dyskwalifikacji. Ingerowanie w przeciwnika jest sprzeczne z duchem rywalizacji.

    Proszę zdefiniować "wbrew duchowi konkurencji" i "ingerowanie w przeciwnika"?

    Również-dla uproszczenia, polecam:

    • zakaz używania CPU w ogóle podczas gniazda CPU przeciwnika.
    • zakazuje równoległości wątków i zamiast daj więcej sekund procesora na jednym wątku. Uprości to programowanie sztucznej inteligencji i nie zaszkodzi nikomu, kto i tak jest związany z procesorem/pamięcią.

    PS-pytanie do post-doków CS: czy ta gra nie jest rozwiązalna (tzn. czy jest jedna, najlepsza strategia?). tak, rozmiar deski i liczba kroków sprawia, że minimax et al obowiązkowe, ale nadal muszę się zastanawiać... to daleko od Go i szachy w złożoności.

     1
    Author: Shachar,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-10-27 15:49:46

    Przewiduję, że osoba, która zdoła inżynierii wstecznej swoich przeciwników losowe nasiona i wzorzec wywołania wygra.

    Nie wiem, czy to jest prawdopodobne.

     1
    Author: Triston Attridge,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-10-27 16:58:07

    Prawdopodobnie możliwe byłoby również uruchomienie serii z wariacjami na temat gry.

    Dodanie takich rzeczy jak samolot 3d lub możliwość poruszania pojedynczym statkiem zamiast strzelać na turę prawdopodobnie zmieniłoby grę Trochę.

     1
    Author: Glenn,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-10-28 02:20:12

    Sekunda Razem czas gry jest specyficzny dla maszyny. Raz drugi wartość operacji CPU będzie inna na mojej maszynie w porównaniu do maszyny turniejowej. Jeśli zoptymalizuję algorytm Battle Ship, aby wykorzystać najwięcej czasu procesora w ciągu 1 sekundy, to jest uruchamiany NA możliwej wolniejszej maszynie turniejowej, zawsze przegrywa.

    Nie jestem pewien, jak obejść to ograniczenie RAM, ale należy je rozwiązać.

    ...

    Jednym z pomysłów jest zrobienie tego, co było zrobione w tej konkurencji http://www.bcsstudentcontest.com /

    I mają maksymalny czas na turę w przeciwieństwie do maksymalnego całkowitego czasu gry. W ten sposób mogłem ograniczyć algorytmy, aby zmieścić się w czasie know turn. Gra może trwać od 50 do 600+ TUR, jeśli mój algorytm zarządza swoim całkowitym czasem gry, może nie dać wystarczająco dużo czasu, aby wykonać najlepszą pracę lub może dać zbyt dużo czasu i stracić. Jest to bardzo trudne do zarządzania całkowity czas gry w algorytmie Pancernik.

    I sugerowałoby to zmianę zasad, aby ograniczyć czas tury, a nie całkowity czas gry.

    Edit

    Jeśli napisałem algorytm, który wylicza wszystkie możliwe ujęcia, a następnie szereguje je, a następnie wykonuje najwyższe ujęcie. Wygenerowanie wszystkich możliwych ujęć zajęłoby zbyt dużo czasu, więc pozwoliłbym algorytmowi działać przez pewien czas, a następnie go zatrzymać.

    Gdyby istniał limit turowy, mógłbym pozwolić algorytmowi działać przez 0,9 sekundy i zwrócić najwyższy strzał rankingowy, i dobrze z ograniczeniem czasu skrętu.

    Jeśli jestem ograniczony do całkowitego czasu gry jednej sekundy, trudno będzie określić, jak długo algorytm powinien działać dla każdej tury. Będę chciał maksymalnie mój czas procesora. Jeśli gra trwała 500 rund mogę ograniczyć każdą turę do 0.002 sekund, ale jeśli gra trwała 100 rund mogę dać każdą turę 0.01 sekund czasu procesora.

    Byłoby niepraktyczne, aby algorytm używał pół-wyczerpującego wyszukiwania przestrzeni strzału, aby znaleźć najlepsze strzał z ograniczeniem prądu.

    1 sekunda całkowitego czasu gry ogranicza Typ algorytmów, które mogą być skutecznie wykorzystane do konkurowania w grze.

     1
    Author: TonyAbell,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-11-01 21:29:37

    Ja się tu czepiam nie wpisując rzeczywistego kodu - ale zaryzykuję jakieś ogólne spostrzeżenia:

    • ponieważ wszystkie statki mają rozmiar co najmniej 2 ogniw, możesz użyć optymalizacji, którą widziałem na implementacji gry w Space Quest V - która wystrzeliwuje tylko alternatywne ogniwa w diamentowym wzorze, podczas gdy "szuka" celu. Eliminuje to połowę kwadratów, jednocześnie gwarantując, że w końcu znajdziesz wszystkie statki.
    • losowy wzorzec strzelania podczas poszukiwania cele statystycznie dadzą najlepsze wyniki w wielu grach.
     1
    Author: Bork Blatt,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-11-02 13:45:02

    ![Gęstość prawdopodobieństwa] [1]enter image description her

    ![wpisz tutaj opis obrazka][2]

    Eksperymentowałem z porównywaniem wyników randon shooting vs głupie polowanie / cel i wreszcie wyszukane wyszukiwanie.

    Najlepszym rozwiązaniem wydaje się stworzenie funkcji gęstości prawdopodobieństwa dla tego, jak prawdopodobne jest, że każdy pojedynczy kwadrat jest używany przez pozostałe statki, i celować z kwadratem o najwyższej wartości.

    Możesz zobaczyć moje wyniki tutaj wpisz link opis tutaj

     1
    Author: Nick Berry,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2011-12-24 21:09:54

    "Pancernik" to coś, co jest znane jako klasyczny komputerowy problem NP-complete.

    Http://en.wikipedia.org/wiki/List_of_NP-complete_problems

    (poszukaj Battleship - jest tam, pod grami i zagadkami)

     -2
    Author: inked,
    Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
    2009-10-28 19:08:53