关于c#:什么是最好的战舰AI?

What is the best Battleship AI?

战舰!

早在2003年(我17岁的时候),我参加了一次战舰人工智能编码比赛。尽管我输掉了那场比赛,但我很开心,从中学到了很多东西。

现在,我想重新展开这场竞赛,寻找最好的战列舰人工智能。

这里是框架,现在托管在BitBucket上。

获胜者将获得+450荣誉!比赛将于2009年11月17日开始。17日0小时后的任何输入或编辑将不被接受。(中央标准时间)尽早提交您的条目,以免错过您的机会!

为了保持这一目标,请遵循竞争精神。

游戏规则:

  • 游戏在10x10网格上进行。
  • 每个参赛者将把5艘船(长度分别为2、3、3、4、5)放在各自的网格上。
  • 没有船只可以重叠,但它们可以是相邻的。
  • 然后,参赛者轮流向对手开一枪。
    • 游戏的一个变种允许每次截击发射多发子弹,每艘幸存的飞船一发。
  • 如果球落下、命中或未命中,对手将通知对手。
  • 当任何一个玩家的所有船只沉没时,游戏结束。
  • 竞赛规则:

  • 竞争的精神是寻找最佳的战列舰算法。
  • 任何违反竞赛精神的行为都将被取消资格。
  • 干涉对手是违背竞争精神的。
  • 多线程可以在以下限制条件下使用:
    • 轮到你的时候,最多只能有一条线在运行。(不过,任何数量的线程都可能处于"挂起"状态)。
    • 除了"正常"外,任何线程都不能以其他优先级运行。
    • 考虑到以上两个限制,在轮到您的时候,您将得到至少3个专用CPU核心的保证。
  • 每个游戏的CPU时间限制为1秒,分配给主线程上的每个竞争对手。
  • 时间用完会导致当前游戏失败。
  • 任何未处理的异常都将导致当前游戏失败。
  • 允许网络访问和磁盘访问,但您可能会发现时间限制相当令人望而却步。但是,为了缓解时间紧张,增加了一些设置和拆卸方法。
  • 代码应该作为答案发布在堆栈溢出上,或者,如果太大,则链接。
  • 条目的最大总大小(未压缩)为1 MB。
  • 官方上,.NET 2.0/3.5是唯一的框架要求。
  • 您的条目必须实现IBattleShippopponent接口。
  • 得分:

  • 101场比赛中最好的51场是一场比赛的胜利者。
  • 所有参赛者将以循环赛的方式进行比赛。
  • 然后,最优秀的一半选手将参加双淘汰赛,以确定胜者。(实际上,二的最小幂大于或等于二分之一。)
  • 我将使用锦标赛的锦标赛框架。
  • 结果将张贴在这里。
  • 如果您提交了多个条目,只有您的最佳评分条目才有资格获得双elim。
  • 祝你好运!玩得高兴!

    编辑1:
    多亏了弗里德,他在Ship.IsValid函数中发现了一个错误。已经修好了。请下载框架的更新版本。

    编辑2:
    由于人们对将统计数据持久化到磁盘等方面非常感兴趣,所以我添加了一些非定时设置和关闭事件,这些事件应该提供所需的功能。这是一个半突破性的变化。也就是说:接口已经修改为添加函数,但不需要主体。请下载框架的更新版本。

    编辑3:
    bug修复1:GameWonGameLost只有在超时的情况下才会被调用。
    错误修复2:如果一个引擎在每场比赛中超时,比赛将永远不会结束。
    请下载框架的更新版本。

    编辑4:
    锦标赛结果:

    ></P></p>
<div class=


    我赞成在每场比赛中做更多的比赛。玩50个游戏就是掷硬币。我需要做1000个游戏来区分测试算法。

    下载无畏1.2。

    策略:

    • 跟踪命中率大于0的船舶的所有可能位置。该列表永远不会超过~30K,因此可以精确保存,这与所有船舶(非常大)的所有可能位置列表不同。

    • getshot算法有两部分,一部分生成随机放炮,另一部分生成随机放炮。试图完成击沉一艘已经被击中的船。如果有一个可能的位置(从上面的列表中),所有被击中的船只都沉没了,我们会随机射击。否则,我们试图通过选择一个位置来完成击沉一艘船,这个位置消除了最可能的位置(加权)。

    • 对于随机放炮,根据其中一艘不明船只与该位置重叠的可能性计算最佳放炮位置。

    • 一种自适应算法,用于将舰船放置在敌方统计上不太可能射击的位置。

    • 一种自适应算法,它喜欢在对手统计上更倾向于放置船只的位置进行射击。

    • 放置船只时,一般不要相互接触。


    这是我的条目!(最天真的解决方案)

    "随机1.1"

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    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() { }
        }
    }


    这里有一个对手可以与之对抗:

    • http://natekohl.net/files/farnsworthopponent.cs

    我认为,与其使用固定的几何学启发的策略,不如尝试估计任何特定的未探测空间容纳船只的潜在概率。

    为了做到这一点,您将探索适合当前世界视图的所有可能的船舶配置,然后根据这些配置计算概率。你可以把它想象成探索一棵树:

    可能的战舰状态扩展http://natekohl.net/media/battleship-tree.png

    在考虑到那棵树的所有叶子与你对世界的了解(例如,船不能重叠,所有击中的方块必须是船等)之后,你可以计算出船在每个未探测位置发生的频率,以估计船坐在那里的可能性。

    这可以被视为热图,热区更可能包含船只:

    每个未探测位置的概率热图http://natekohl.net/media/battleship-probs.png

    我喜欢这场战舰竞赛的一点是,上面的树几乎小到可以暴力使用这种算法。如果这5艘船每艘有大约150个可能的位置,那就是1505=750亿个可能的位置。这个数字只会变小,特别是如果你能消灭整艘船。

    我在上面提到的对手并没有探索整棵树;750亿到现在还很难进入。不过,它确实试图借助一些启发式方法来估计这些概率。


    这不是一个成熟的答案,但似乎没有什么必要用常见的代码来混淆真正的答案。因此,我以开源的精神介绍了一些扩展/通用类。如果使用这些,请更改命名空间或尝试将所有内容编译为一个dll将不起作用。

    BoardView允许您轻松使用带注释的板。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    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
        }
    }

    一些扩展,其中一些功能在主框架中是重复的,但实际上应该由您来完成。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    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));
            }
        }
    }

    我最终用了很多东西。

    1
    2
    3
    4
    5
    6
    7
    enum OpponentsBoardState
    {
        Unknown = 0,
        Miss,
        MustBeEmpty,        
        Hit,
    }

    随机化。安全但可测试,用于测试。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    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!");
            }
        }
    }


    没有什么比这更复杂的了,这就是我的想法。它在99.9%的时间里击败随机对手。如果有人有任何其他的小挑战像这样,它是很有趣的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    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() { }
        }
    }

    稍微压缩以占用这里的最小空间,并且仍然是可读的。


    我现在没有时间来写一个完整的算法,但这里有一个想法:如果你的对手随机放置船只,放置概率不是一个简单的分布集中在(5.5,5.5)?例如,战列舰(5个单位长)在X维中的放置可能性如下:

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

    同样的计算对Y也是有效的。其他的船不会有这么陡的分布,但是你最好的猜测仍然是中心。在那之后,数学方法将缓慢地从中心向外辐射对角线(可能是平均船长17/5)。前任:

    1
    2
    3
    4
    5
    ...........
    ....x.x....
    .....x.....
    ....x.x....
    ...........

    很明显,这个想法需要增加一些随机性,但我认为纯粹从数学上来说,这是可行的方法。


    关于竞争引擎的一些评论:

    新游戏参数:

    如果iBattleShipOpponent::NewGame用于游戏前设置并采用BoardSize,则它还应采用船只及其各自大小的列表。如果不考虑不同的船舶配置,那么允许不同的船板尺寸是没有意义的。

    船舶密封:

    我看不出为什么班轮被封了。除了其他基本的事情,我希望船有一个名字,所以我可以输出消息,比如("你沉没了我的0",ship.name);。我也在考虑其他扩展,所以我认为船应该是可继承的。

    时间限制:

    虽然1秒的时间限制对于锦标赛规则来说是有意义的,但它完全妨碍了调试。战列舰竞赛应该有一个简单的设置来忽略时间违规,以帮助开发/调试。我还建议调查system.diagnostics.process::userprocessortime/privileged processortime/totalprocessortime,以更准确地查看正在使用的时间。

    沉船:

    当前的API会在您击沉对手的船时通知您:

    1
    ShotHit(Point shot, bool sunk);

    但不是你沉没的那艘船!我认为这是人类战舰规则的一部分,你必须宣布"你击沉了我的战舰!"(或驱逐舰、潜艇等)。

    当一个人工智能试图冲出相互碰撞的船只时,这一点尤其重要。我想请求将API更改为:

    1
    ShotHit(Point shot, Ship ship);

    如果船是非空的,这意味着射击是一个下沉射击,你知道你沉没的船,以及它是多久。如果射击是非下沉射击,那么船是空的,你没有进一步的信息。


    CrossFire已更新。我知道它不能与法恩斯沃思或无畏竞争,但它比法恩斯沃思快得多,而且在任何人想尝试的情况下都很容易玩。这依赖于我的库的当前状态,包括在这里以便于使用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    461
    462
    463
    464
    465
    466
    467
    468
    469
    470
    471
    472
    473
    474
    475
    476
    477
    478
    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));
            }
        }

    }


    我的电脑现在正由戴尔修理,但这是我上周的工作地点:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    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; }
        }
    }


    这是我在空闲时间所能做的最好的事情,这是不存在的。有一些游戏和比赛统计数据正在进行,因为我设置了循环的主要功能,并不断运行战列舰比赛,直到我按下一个键。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    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();
            }
        }
    }

    这个逻辑是我击败无畏者最接近的,赢得了41%的个人比赛。(事实上,它以52比49赢得了一场比赛。)奇怪的是,这门课与法恩斯沃思霍本特的比赛不如以前的那门课先进得多。


    如果你是粗暴地强迫你的分析,那么你可能会发现所提供的随机支持的机制非常低效。它允许自己重新选择已经定位的位置,并允许框架强制它重复,直到它碰到一个尚未触及的位置,或者每次移动的时间限制到期。

    这个对手有类似的行为(有效的位置分布是相同的),它只进行健全性检查本身,每次调用只消耗一个随机数生成(分摊)。

    这将使用扩展/库应答中的类,并且我只提供关键方法/状态。

    洗牌是从乔恩·斯基特的回答中提出来的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    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
  • 击中1号点,一个在中间的右边
  • 击中上一次击中右侧的第四个点
  • 继续这样的模式(应该以三个空格分隔的对角线结束),这应该击中所有4和5条长度的船,以及统计上大量的3和2条船。

  • 开始随机打击对角线之间的点,这将抓住2和3长度的船,还没有注意到。

  • 一旦我检测到5次撞击,我就会确定5次撞击是否在不同的船上。在两次命中位于同一水平线或垂直线上且彼此在5个位置内(可能是同一艘船上的两次命中)的位置附近再进行几次射击相对容易。如果它们是分开的船,那么继续下沉所有的船。如果发现它们是同一条船,继续上面的填充模式,直到所有5条船都就位。

    该算法是一种简单的填充算法。它的主要特点是,它不会浪费时间沉船,它知道什么时候还有它不知道的船,也不会使用低效的填充模式(即,完全随机的模式将是浪费)。

    最后注:

    a)"中心"是板上的随机起点。这消除了该算法的主要弱点。b)虽然描述表明从一开始就立即绘制对角线,但理想情况下,算法仅在沿着这些对角线的"随机"位置拍摄。这有助于防止竞争对手对其船只受到可预测模式撞击的时间进行计时。

    这描述了一个"完美"的算法,它可以让所有的船在(9x9)/2+10次射击下。

    但是,可以显著改善:

    一旦一艘船被撞击,在做"内部"对角线之前要确定它的大小。你可能已经找到了2号船,在这种情况下,内部对角线可以简化,以更快地找到3号船。

    确定游戏的阶段并采取相应的行动。这个算法在游戏中的某一点上可能是好的,但是作为最终游戏的一部分,其他算法可能会产生更好的好处。另外,如果另一个玩家非常接近打败你,另一个算法可能会更好地工作-例如,高风险算法可能会更频繁地失败,但当它工作得很快时,你可能击败比你更接近胜利的对手。

    确定竞争对手的游戏风格-这可能会给你提供他们如何计划船舶布置的线索(即,他们自己的算法很可能最快地确定他们如何布置自己的船舶-如果你唯一拥有的工具是锤子,一切看起来都像钉子)

    -亚当


    我的条目。

    没有什么特别的,我也没有时间去补充我所有的好主意。

    但它似乎玩得相当好。我们将看到它在竞争中的表现:

    (将其放入文件Missouri.cs中并添加到项目中。)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
    319
    320
    321
    322
    323
    324
    325
    326
    327
    328
    329
    330
    331
    332
    333
    334
    335
    336
    337
    338
    339
    340
    341
    342
    343
    344
    345
    346
    347
    348
    349
    350
    351
    352
    353
    354
    355
    356
    357
    358
    359
    360
    361
    362
    363
    364
    365
    366
    367
    368
    369
    370
    371
    372
    373
    374
    375
    376
    377
    378
    379
    380
    381
    382
    383
    384
    385
    386
    387
    388
    389
    390
    391
    392
    393
    394
    395
    396
    397
    398
    399
    400
    401
    402
    403
    404
    405
    406
    407
    408
    409
    410
    411
    412
    413
    414
    415
    416
    417
    418
    419
    420
    421
    422
    423
    424
    425
    426
    427
    428
    429
    430
    431
    432
    433
    434
    435
    436
    437
    438
    439
    440
    441
    442
    443
    444
    445
    446
    447
    448
    449
    450
    451
    452
    453
    454
    455
    456
    457
    458
    459
    460
    461
    462
    463
    464
    465
    466
    467
    468
    469
    470
    471
    472
    473
    474
    475
    476
    477
    478
    479
    480
    481
    482
    483
    484
    485
    486
    487
    488
    489
    490
    491
    492
    493
    494
    495
    496
    497
    498
    499
    500
    501
    502
    503
    504
    505
    506
    507
    508
    509
    510
    511
    512
    513
    514
    515
    516
    517
    518
    519
    520
    521
    522
    523
    524
    525
    526
    527
    528
    529
    530
    531
    532
    533
    534
    535
    536
    537
    538
    539
    540
    541
    542
    543
    544
    545
    546
    547
    548
    549
    550
    551
    552
    553
    554
    555
    556
    557
    558
    559
    560
    561
    562
    563
    564
    565
    566
    567
    568
    569
    570
    571
    572
    573
    574
    575
    576
    577
    578
    579
    580
    581
    582
    583
    584
    585
    586
    587
    588
    589
    590
    591
    592
    593
    594
    595
    596
    597
    598
    599
    600
    601
    602
    603
    604
    605
    606
    607
    608
    609
    610
    611
    612
    613
    614
    615
    616
    617
    618
    619
    620
    621
    622
    623
    624
    625
    626
    627
    628
    629
    630
    631
    632
    633
    634
    635
    636
    637
    638
    639
    640
    641
    642
    643
    644
    645
    646
    647
    648
    649
    650
    651
    652
    653
    654
    655
    656
    657
    658
    659
    660
    661
    662
    663
    664
    665
    666
    667
    668
    669
    670
    671
    672
    673
    674
    675
    676
    677
    678
    679
    680
    681
    682
    683
    684
    685
    686
    687
    688
    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 ="   +----+----+----+----+----+----+----+----+----+----+
    "
    ;
                String disp;
                int x,y;

                output +="      A    B    C    D    E    F    G    H    I    J    
    "
    + 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 +="|
    "
    + 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}
    "
    , Target.Length);
                String horzDiv ="   +----+----+----+----+----+----+----+----+----+----+
    "
    ;
                int x,y;

                output +="      A    B    C    D    E    F    G    H    I    J    
    "
    + 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 +="|
    "
    + 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 ="   +----+----+----+----+----+----+----+----+----+----+
    "
    ;
                String disp;
                int x,y;

                output +="      A    B    C    D    E    F    G    H    I    J    
    "
    + 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 +="|
    "
    + 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;
        }
    }


    事实上,我认为拼图最大的问题在于它本质上是两个动作。一个动作是放置你的飞船,另一个动作是寻找敌人的飞船(不管第二部分是如何分割的,除了试图用一个随机因素打败一个时钟之外,它只是"运行你的算法")。没有机制来决定和对抗敌人的战略,这就是为什么类似的比赛围绕着连续几轮的"石头剪刀"而展开相当有趣。

    另外,我认为如果你将游戏指定为一个网络协议,然后在C中提供实现该协议的框架,而不是规定所有的解决方案都应该是C,这会更酷,但这只是我的观点。

    编辑:我取消了我的起点,因为我没有仔细阅读比赛规则。


    我总是喜欢从中间开始,从那一点开始,在任何其他点之间留下不超过1个空格来解释那该死的子……射击之间的距离取决于沉船的位置。如果B型船是最后一艘,那么射击只需要在中间留出4个空位,以尽量减少浪费的射击。


    萨里大学的詹姆斯·希瑟博士代表英国计算机协会举办了一场类似的竞赛。

    对资源施加了限制——即每轮的最大处理器时间、移动之间无法存储任何状态、施加的最大堆大小。为了限制时间,人工智能可以在时段内的任何点提交移动,并在转弯结束时要求移动。

    非常有趣-更多信息请访问:http://www.bcsstudentcast.com/

    可能会给你更多的想法。


    这不是极大极小值。其实放完船后,难道每个玩家都不能自己玩吗,结果就轮到他去击沉每一个对手的船了吗?掉头少的人获胜。

    我不认为有什么好的一般策略,除了击沉被击中的船只和尽量减少射击次数,以覆盖船只可能藏匿的其他地方。

    当然,对于任何非随机的事情,可能都有反策略。但我不认为有什么策略对所有可能的球员都是好的。


    事实上,在Ubuntu9.10Linux中,该解决方案在MonoDevelopment中不做任何修改即可打开和运行。


    这也可能,大概是有可能运行一系列的这些变化的游戏。

    加入一些东西,比如一个3D飞机或者能够移动一艘飞船而不是为一个转弯而射击,可能会对游戏有很大的改变。


    ![概率密度][1]输入图像描述

    ![在此处输入图像描述][2]

    我尝试比较Randon射击和一个愚蠢的狩猎/目标的结果,最后是一个复杂的搜索。

    最好的解决方案似乎是创建一个概率密度函数,用于计算剩余船只使用任何单个正方形的可能性,并以具有最大值的正方形为目标。

    你可以在这里看到我的结果在这里输入链接描述


    我预测,成功反向工程他们的对手随机种子和调用模式的人将获胜。

    但不确定这有多可能。


    我不把实际的代码放进去,但我会冒一些一般性的风险:

    • 因为所有飞船的大小都至少有2个单元,所以你可以使用我在太空探索v中看到的一个游戏实现的优化——它只在"寻找"目标的时候以菱形模式向备用单元开火。这消除了一半的方块,同时仍然保证你最终会找到所有的船。
    • 搜索目标时的随机射击模式将在统计上在许多游戏中产生最佳结果。

    你写道:

    • 任何违反竞赛精神的行为都将被取消资格。
    • 干涉对手是违背竞争精神的。

    请定义"违背竞争精神"和"干扰对手"?

    另外-为了简化,我建议您:

    • 禁止在对手的CPU插槽中使用CPU。
    • 不允许线程并行,而是在单个线程上提供更多的CPU时间。这将简化人工智能的编程,并且不会伤害任何CPU/内存受限的人。

    PS-这是一个关于CS Post Docs的问题:这个游戏是否可以解决(即,是否有一个单一的,最好的策略?)是的,电路板的尺寸和步数使得minimax等成为强制性的,但我仍然想知道…在复杂性方面,这远非易事和下棋。


    总游戏时间为1秒,具体取决于机器。一旦第二次的CPU操作将是不同的,在我的机器与锦标赛机器。如果我优化了战船算法,使其在1秒内使用最多的CPU时间,那么它将运行在可能较慢的锦标赛机器上,它将永远失败。

    我不确定如何绕过这个框架的局限性,但应该加以解决。

    一个想法是做在这个比赛中做的事情http://www.bcsstudentcast.com/

    每回合的最大时间,而不是游戏的最大总时间。这样我就可以限制算法在一个已知的转弯时间内。一个游戏可能会持续50到600次以上的回合,如果我的算法管理它的总游戏时间,它可能不会给足够的时间做它最好的工作,或者它可能会给太多的时间和损失。在战列舰算法中,很难对总博弈时间进行管理。

    我建议改变规则来限制回合时间,而不是总的比赛时间。

    编辑

    如果我写了一个算法,列出所有可能的快照,然后对它们进行排序,那么就得到最高级别的快照。生成所有可能的快照需要很长时间,所以我会让算法运行一定的时间,然后停止它。

    如果有一个基于回合的限制,我可以让算法运行0.9秒,并返回最高排名的镜头,并很好地应付回合的时间限制。

    如果我被限制在一秒钟的总游戏时间内,就很难确定算法在每一回合中应该运行多长时间。我想最大限度地利用我的CPU时间。如果一个游戏持续500回合,我可以把每一回合限制在0.002秒,但是如果一个游戏持续100回合,我可以给每一回合0.01秒的CPU时间。

    对于一个算法来说,用半穷举搜索镜头空间来寻找当前限制下的最佳镜头是不切实际的。

    游戏总时间为1秒,这限制了可有效用于在游戏中竞争的算法类型。


    "战舰"是一个典型的计算机科学NP完全问题。

    http://en.wikipedia.org/wiki/list_-np-complete_问题

    (寻找战列舰-它在那里,在游戏和谜题下)