Chiến hạm AI tốt nhất là gì?


315

Tàu chiến!

Quay trở lại năm 2003 (khi tôi 17 tuổi), tôi đã tham gia cuộc thi AI Battleship cuộc thi viết mã . Mặc dù tôi đã thua giải đấu đó, tôi đã có rất nhiều niềm vui và học được rất nhiều từ nó.

Bây giờ, tôi muốn hồi sinh cuộc thi này, trong cuộc tìm kiếm tàu ​​chiến AI tốt nhất.

Đây là khung, hiện được lưu trữ trên Bitbucket .

Người chiến thắng sẽ được trao +450 danh tiếng! Cuộc thi sẽ được tổ chức bắt đầu vào ngày 17 tháng 11 năm 2009 . Không có mục hoặc chỉnh sửa muộn hơn 0 giờ vào ngày 17 sẽ được chấp nhận. (Giờ chuẩn miền trung) Gửi bài dự thi sớm, vì vậy bạn đừng bỏ lỡ cơ hội của mình!

Để giữ MỤC TIÊU này , xin vui lòng làm theo tinh thần của cuộc thi.

Luật chơi:

  1. Trò chơi được chơi trên lưới 10x10.
  2. Mỗi đối thủ sẽ đặt mỗi 5 tàu (có chiều dài 2, 3, 3, 4, 5) trên lưới của họ.
  3. Không có tàu có thể chồng lên nhau, nhưng chúng có thể liền kề.
  4. Các đối thủ sau đó thay phiên nhau bắn một phát vào đối thủ của họ.
    • Một biến thể trong trò chơi cho phép bắn nhiều phát mỗi cú, một quả cho mỗi tàu còn sống.
  5. Đối thủ sẽ thông báo cho đối thủ nếu cú ​​đánh chìm, trúng hoặc bỏ lỡ.
  6. Chơi trò chơi kết thúc khi tất cả các tàu của bất kỳ một người chơi nào bị chìm.

Thể lệ cuộc thi:

  1. Tinh thần của cuộc thi là tìm ra thuật toán Battleship tốt nhất.
  2. Bất cứ điều gì được coi là trái với tinh thần của cuộc thi sẽ là căn cứ để bị loại.
  3. Can thiệp vào một đối thủ là trái với tinh thần của cuộc thi.
  4. Đa luồng có thể được sử dụng theo các hạn chế sau:
    • Không có nhiều hơn một luồng có thể đang chạy trong khi đó không phải là lượt của bạn. (Mặc dù, bất kỳ số lượng chủ đề có thể ở trạng thái "Bị treo").
    • Không có chủ đề có thể chạy ở mức ưu tiên khác ngoài "Bình thường".
    • Với hai hạn chế trên, bạn sẽ được đảm bảo ít nhất 3 lõi CPU chuyên dụng trong lượt của mình.
  5. Giới hạn 1 giây thời gian CPU cho mỗi trò chơi được phân bổ cho từng đối thủ trên luồng chính.
  6. Hết thời gian dẫn đến mất trò chơi hiện tại.
  7. Bất kỳ ngoại lệ chưa được xử lý sẽ dẫn đến mất trò chơi hiện tại.
  8. Truy cập mạng và truy cập đĩa được cho phép, nhưng bạn có thể thấy các giới hạn thời gian khá cấm. Tuy nhiên, một vài phương pháp thiết lập và phá bỏ đã được thêm vào để giảm bớt sự căng thẳng về thời gian.
  9. Mã phải được đăng trên stack stack như một câu trả lời, hoặc, nếu quá lớn, được liên kết.
  10. Tổng kích thước tối đa (chưa nén) của mục nhập là 1 MB.
  11. Chính thức, .Net 2.0 / 3.5 là yêu cầu khung duy nhất.
  12. Mục nhập của bạn phải thực hiện giao diện IBattleshipOpponent.

Ghi điểm:

  1. 51 trò chơi hay nhất trong số 101 trò chơi là người chiến thắng trong một trận đấu.
  2. Tất cả các đối thủ sẽ thi đấu với nhau, theo kiểu vòng tròn.
  3. Một nửa tốt nhất của các đối thủ sau đó sẽ chơi một giải đấu loại bỏ hai lần để xác định người chiến thắng. (Thực tế sức mạnh nhỏ nhất của hai lớn hơn hoặc bằng một nửa.)
  4. Tôi sẽ sử dụng TournamentApi khuôn khổ cho giải đấu.
  5. Kết quả sẽ được đăng ở đây.
  6. Nếu bạn gửi nhiều mục, chỉ có mục ghi điểm cao nhất của bạn mới đủ điều kiện để loại bỏ hai lần.

Chúc may mắn! Chúc vui vẻ!


EDIT 1:
Nhờ Freed , người đã tìm thấy một lỗi trong Ship.IsValidchức năng. Nó đã được sửa chữa. Vui lòng tải về phiên bản cập nhật của khung.

EDIT 2:
Vì đã có sự quan tâm đáng kể trong việc duy trì các số liệu thống kê vào đĩa và do đó, tôi đã thêm một vài sự kiện thiết lập và xé không theo thời gian sẽ cung cấp chức năng cần thiết. Đây là một thay đổi bán phá vỡ . Điều đó có nghĩa là: giao diện đã được sửa đổi để thêm các chức năng, nhưng không có phần thân nào được yêu cầu cho chúng. Vui lòng tải về phiên bản cập nhật của khung.

EDIT 3:
Bug Fix 1: GameWonGameLostchỉ được gọi trong trường hợp hết thời gian.
Sửa lỗi 2: Nếu một công cụ đã hết thời gian trong mọi trò chơi, cuộc thi sẽ không bao giờ kết thúc.
Vui lòng tải về phiên bản cập nhật của khung.

EDIT 4:
Kết quả giải đấu:


Nếu mục yêu cầu một cơ sở dữ liệu lớn, nó có thể kết nối với nó qua mạng không? I E. mục có thể thực hiện cuộc gọi dịch vụ web?
Remus Rusanu

Có giới hạn kích thước trên các mục?
Jherico

8
@Steven: Ngoài ra, tôi đã hỏi ý kiến ​​Jeff Atwood chỉ để xem nó có phù hợp không. Đây là câu trả lời của anh ấy: twitter.com/codinghorror/status/5203185621
John Gietzen

1
Ngoài ra tôi sẽ thêm taht, với thành phần ngẫu nhiên không thể tránh khỏi cho 50 trò chơi này sẽ không đủ để phân biệt chính xác giữa các triển khai rất tốt. Tôi nghĩ rằng 501 hoặc nhiều hơn có thể là cần thiết cho một cái nhìn hợp lý về cái nào tốt hơn.
ShuggyCoUk

1
Một đối thủ "hòa bình" không chịu đặt tàu khiến cuộc thi bị treo. Không chắc bạn quan tâm đến việc mọi người làm những điều ngớ ngẩn như thế. :)
Joe

Câu trả lời:


56

Tôi thứ hai chuyển động để làm nhiều trò chơi hơn mỗi trận đấu. Làm 50 trò chơi chỉ là lật một đồng xu. Tôi cần phải làm 1000 trò chơi để có được sự phân biệt hợp lý giữa các thuật toán thử nghiệm.

Tải xuống Dreadn think 1.2 .

Chiến lược:

  • theo dõi tất cả các vị trí có thể cho các tàu có> 0 lượt truy cập. Danh sách không bao giờ lớn hơn ~ 30K vì vậy nó có thể được giữ chính xác, không giống như danh sách tất cả các vị trí có thể cho tất cả các tàu (rất lớn).

  • Thuật toán GetShot có hai phần, một phần tạo ra các bức ảnh ngẫu nhiên và phần còn lại cố gắng hoàn thành việc đánh chìm một con tàu đã bị tấn công. Chúng tôi thực hiện các cú đánh ngẫu nhiên nếu có một vị trí có thể (từ danh sách trên) trong đó tất cả các tàu bị đánh chìm. Mặt khác, chúng tôi cố gắng hoàn thành việc đánh chìm một con tàu bằng cách chọn một địa điểm để bắn vào đó loại bỏ các vị trí có thể nhất (có trọng số).

  • Đối với các ảnh chụp ngẫu nhiên, hãy tính toán vị trí tốt nhất để bắn dựa trên khả năng một trong những con tàu không được xếp chồng lên vị trí đó.

  • thuật toán thích ứng đặt tàu ở những vị trí mà đối thủ ít có khả năng bắn.

  • thuật toán thích ứng thích bắn vào các vị trí mà đối thủ có nhiều khả năng đặt tàu của mình hơn.

  • nơi tàu hầu như không chạm vào nhau.


trên máy thử nghiệm của tôi (netbook ULV Celeron) mã này bị mất do hết thời gian chờ. Khi tôi để nó mất toàn bộ thời gian nó muốn nó đánh đòn Đơn giản (tỷ lệ thành công khoảng 90%). Nếu bạn đang phụ thuộc nhiều vào thông số kỹ thuật của máy bạn sẽ chạy để đánh vào thời gian của bạn, bạn có thể muốn cho mình một số phòng ngọ nguậy ...
ShuggyCoUk

Thật thú vị ... Nó chạy tốt trên máy giải đấu. Tuy nhiên, một động cơ "hoàn hảo" sẽ thích nghi với bao nhiêu thời gian nó đã bỏ ra.
John Gietzen

35

Đây là mục của tôi! (Giải pháp ngây thơ nhất có thể)

"Ngẫu nhiên 1.1"

namespace Battleship
{
    using System;
    using System.Collections.ObjectModel;
    using System.Drawing;

    public class RandomOpponent : IBattleshipOpponent
    {
        public string Name { get { return "Random"; } }
        public Version Version { get { return this.version; } }

        Random rand = new Random();
        Version version = new Version(1, 1);
        Size gameSize;

        public void NewGame(Size size, TimeSpan timeSpan)
        {
            this.gameSize = size;
        }

        public void PlaceShips(ReadOnlyCollection<Ship> ships)
        {
            foreach (Ship s in ships)
            {
                s.Place(
                    new Point(
                        rand.Next(this.gameSize.Width),
                        rand.Next(this.gameSize.Height)),
                    (ShipOrientation)rand.Next(2));
            }
        }

        public Point GetShot()
        {
            return new Point(
                rand.Next(this.gameSize.Width),
                rand.Next(this.gameSize.Height));
        }

        public void NewMatch(string opponent) { }
        public void OpponentShot(Point shot) { }
        public void ShotHit(Point shot, bool sunk) { }
        public void ShotMiss(Point shot) { }
        public void GameWon() { }
        public void GameLost() { }
        public void MatchOver() { }
    }
}

52
Trên thực tế, câu trả lời này rất hay vì nó thể hiện ở dạng rất ngắn gọn mà API bạn cần triển khai để cạnh tranh ... :)
dicroce

1
Quay lại khi tôi xây dựng một dự án tương tự trong lớp Thuật toán đại học của mình, tôi đã sử dụng logic ngẫu nhiên xen kẽ với một số quyết định. Đôi khi thật tốt!
Nathan Taylor

2
Điều này có thể cố gắng đặt tàu chồng lên nhau không?

6
Có, nhưng động cơ sẽ không cho phép điều này. Sau đó, nó sẽ nói với AI để đặt chúng một lần nữa, nhưng lần này, với một giọng nói nghiêm khắc hơn. (Nhìn thấy pop ax \ cmp ax, 1 \ je stern)
John Gietzen

5
Lưu ý quan trọng đối với bất kỳ ai, giống như tôi, cho rằng họ có thể dễ dàng đánh bại điều này bằng cách nhớ các bức ảnh được đặt trước đó và không lặp lại. Khung sẽ bỏ qua các lần lặp lại và cung cấp cho bạn một cơ hội khác miễn là tổng thời gian của bạn thấp hơn giới hạn. Theo tôi thì điều này thật tệ, nếu ai đó làm rối tung cái tôi của họ thì họ sẽ bị phạt ...
ShuggyCoUk

22

Đây là một đối thủ để mọi người chơi với:

Thay vì sử dụng một chiến lược lấy cảm hứng hình học cố định, tôi nghĩ sẽ rất thú vị khi cố gắng ước tính các xác suất cơ bản mà bất kỳ không gian chưa được khám phá cụ thể nào giữ một con tàu.

Để làm điều này đúng, bạn sẽ khám phá tất cả các cấu hình có thể có của tàu phù hợp với quan điểm hiện tại của bạn về thế giới và sau đó tính toán xác suất dựa trên các cấu hình đó. Bạn có thể nghĩ về nó giống như khám phá một cái cây:

mở rộng các quốc gia chiến hạm có thể http://natekohl.net/media/battleship-tree.png

Sau khi xem xét tất cả các lá của cây đó với những gì bạn biết về thế giới (ví dụ: tàu không thể chồng lên nhau, tất cả các ô vuông phải là tàu, v.v.) bạn có thể đếm tần suất tàu xảy ra ở mỗi vị trí chưa được khám phá để ước tính khả năng Một con tàu đang ngồi ở đó.

Điều này có thể được hình dung như một bản đồ nhiệt, nơi các điểm nóng có nhiều khả năng chứa tàu:

bản đồ nhiệt về xác suất cho từng vị trí chưa được khám phá http://natekohl.net/media/battleship-probs.png

Một điều tôi thích về cuộc thi Battleship này là cái cây ở trên gần như đủ nhỏ để chế tạo loại thuật toán này. Nếu có ~ 150 vị trí có thể cho mỗi trong số 5 tàu, thì đó là 150 5 = 75 tỷ khả năng. Và con số đó chỉ trở nên nhỏ hơn, đặc biệt nếu bạn có thể loại bỏ toàn bộ tàu.

Đối thủ mà tôi liên kết ở trên không khám phá toàn bộ cây; 75 tỷ vẫn còn lớn để có được dưới một giây. Tuy nhiên, nó cố gắng ước tính các xác suất này với sự giúp đỡ của một vài phương pháp phỏng đoán.


Cho đến nay, bạn đang đánh bại giải pháp đầy đủ duy nhất khác của chúng tôi khoảng 67,7% đến 32,3% :)
John Gietzen

2
Tôi chắc chắn tò mò muốn xem "cách tiếp cận xác suất" so với "cách tiếp cận hình học" như thế nào. Tôi đã nhận thấy rằng đối thủ xác suất này thực sự thực hiện các động tác tuân theo các mô hình hình học được thảo luận trong các câu trả lời khác. Có thể là sử dụng hình học cũng tốt như vậy, và nhanh hơn rất nhiều. :)
Nate Kohl

12

Không phải là một câu trả lời đầy đủ nhưng có vẻ như rất ít điểm lộn xộn các câu trả lời thực sự với mã là phổ biến. Do đó tôi trình bày một số phần mở rộng / các lớp chung theo tinh thần của nguồn mở. Nếu bạn sử dụng chúng thì hãy thay đổi không gian tên hoặc cố gắng biên dịch mọi thứ thành một dll sẽ không hoạt động.

BoardView cho phép bạn dễ dàng làm việc với một bảng chú thích.

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
    }
}

Một số tiện ích mở rộng, một số chức năng trùng lặp này trong khung chính nhưng thực sự nên được thực hiện bởi bạn.

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));
        }
    }
}

Một cái gì đó tôi cuối cùng sử dụng rất nhiều.

enum OpponentsBoardState
{
    Unknown = 0,
    Miss,
    MustBeEmpty,        
    Hit,
}

Ngẫu nhiên. An toàn nhưng có thể kiểm tra, hữu ích để thử nghiệm.

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!");
        }
    }
}

10

Bây giờ tôi không có thời gian để viết một thuật toán chính thức, nhưng đây là một suy nghĩ: nếu đối thủ của bạn đặt tàu ngẫu nhiên, thì xác suất vị trí có phải là một phân phối đơn giản tập trung tại (5.5,5.5) không? Ví dụ: các khả năng vị trí cho tàu chiến (dài 5 đơn vị) trong kích thước x có ở đây:

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

Các tính toán tương tự sẽ có giá trị cho y. Các tàu khác sẽ không có nhiều phân phối, nhưng dự đoán tốt nhất của bạn vẫn là trung tâm. Sau đó, phương pháp toán học sẽ từ từ tỏa ra các đường chéo (có lẽ với chiều dài của con tàu trung bình, 17/5) ra khỏi trung tâm. Ví dụ:

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

Rõ ràng một số ngẫu nhiên sẽ cần phải được thêm vào ý tưởng, nhưng tôi nghĩ rằng đó hoàn toàn là về mặt toán học là cách để đi.


Vâng, thực sự họ sẽ. Động cơ cũ của tôi đã bù đắp cho điều đó.
John Gietzen

1
Nơi tôi đến, từ từ tỏa ra các đường chéo ra khỏi trung tâm được coi là gian lận .
bzlm

Nếu nó được coi là gian lận, có một biện pháp đối phó khá dễ dàng. Tránh (x, y) trong đó x = y. :)
ine

5
Tôi nghĩ rằng anh ta đã ám chỉ đếm thẻ? Mà theo tôi, không phải là gian lận.
John Gietzen

10

Không có gì tinh vi nhưng đây là những gì tôi nghĩ ra. Nó đánh bại đối thủ ngẫu nhiên 99,9% thời gian. Sẽ được quan tâm nếu bất cứ ai có bất kỳ thử thách nhỏ như thế này, đó là niềm vui tốt.

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() { }
    }
}

Hơi ngưng tụ để chiếm không gian tối thiểu ở đây và vẫn có thể đọc được.


6

Một số ý kiến ​​về Công cụ cạnh tranh:

Tham số NewGame:

Nếu IBattleshipOpponent :: NewGame được thiết kế để thiết lập trước trò chơi và lấy một tấm ván, thì nó cũng nên có một danh sách các tàu và kích cỡ tương ứng của chúng. Thật vô nghĩa khi cho phép thay đổi kích thước bảng mà không cho phép cấu hình tàu biến.

Tàu được niêm phong:

Tôi không thấy bất kỳ lý do tại sao lớp tàu được niêm phong. Trong số những điều cơ bản khác, tôi muốn Tàu có Tên, vì vậy tôi có thể xuất các tin nhắn như ("Bạn đã chìm {0}", ship.Name); . Tôi cũng có những phần mở rộng khác, vì vậy tôi nghĩ Ship nên được kế thừa.

Giới hạn thời gian:

Mặc dù giới hạn thời gian là 1 giây có ý nghĩa đối với quy tắc giải đấu, nhưng nó hoàn toàn gây rối với việc gỡ lỗi. BattleshipCompetition nên có một cài đặt dễ dàng để bỏ qua các vi phạm thời gian để hỗ trợ phát triển / gỡ lỗi. Tôi cũng sẽ đề nghị điều tra System.Diagnostics.Process :: UserProcessorTime / Đặc quyền ProcessorTime / TotalProcessorTime để có cái nhìn chính xác hơn về thời gian sử dụng.

Tàu chìm:

API hiện tại thông báo cho bạn khi bạn đánh chìm con tàu của oppenent:

ShotHit(Point shot, bool sunk);

nhưng không phải tàu bạn chìm! Tôi coi đó là một phần của quy tắc Chiến hạm con người mà bạn bắt buộc phải tuyên bố "Bạn đã đánh chìm Chiến hạm của tôi!" (hoặc tàu khu trục, hoặc phụ, v.v.).

Điều này đặc biệt quan trọng khi một AI đang cố gắng tuôn ra những con tàu đối đầu với nhau. Tôi muốn yêu cầu thay đổi API thành:

ShotHit(Point shot, Ship ship);

Nếu con tàu không có giá trị, điều đó có nghĩa là cú đánh là một cú bắn chìm và bạn biết con tàu nào bị chìm và thời gian là bao lâu. Nếu phát bắn là một phát bắn không chìm, thì tàu là vô giá trị và bạn không có thêm thông tin nào.


Vui lòng gửi mẫu mã nếu bạn nghĩ rằng thời gian có thể được thực hiện chính xác hơn. Tôi không muốn thay đổi các quy tắc quá nhiều ngay bây giờ.
John Gietzen

Ngoài ra, kích thước tàu được truyền vào trong PlaceShips () được chạy chính xác một lần cho mỗi trò chơi và cũng có thể được sử dụng làm giai đoạn thiết lập. Xin vui lòng hủy niêm phong con tàu để thử nghiệm của riêng bạn, nhưng tôi có kế hoạch sử dụng một con dấu cho giải đấu.
John Gietzen

LGI: @John Gietzen: Tôi đã xác định rằng PlaceShips KHÔNG được chạy chính xác một lần cho mỗi trò chơi (như bạn đã nêu). Nếu người chơi đặt tàu của họ không chính xác (như RandomOpponent thường làm), thì PlaceShips được gọi liên tục mà không có cuộc gọi NewGame can thiệp.
abelenky

5
Tôi luôn coi đó là chiến lược đặt hai tàu trong cấu hình L để khiến đối thủ của tôi nghĩ rằng họ đã đánh chìm một tàu chiến trong khi thực tế họ đã không làm vậy. Tôi chưa bao giờ dưới ấn tượng bạn phải tuyên bố chiếc thuyền nào bị chìm.
Josh Smeaton

3
@DJ: Tôi đang tuân theo các quy tắc bút và giấy gốc. Hãy nhớ rằng Hasbro là một công ty đồ chơi và trò chơi này có trước Hasbro.
John Gietzen

5

Cập nhật CrossFire. Tôi biết nó không thể cạnh tranh với Farnsworth hoặc Dreadn think nhưng nó nhanh hơn rất nhiều so với cái sau và đơn giản để chơi trong trường hợp bất cứ ai muốn thử. Điều này phụ thuộc vào trạng thái hiện tại của các thư viện của tôi, được bao gồm ở đây để làm cho nó dễ sử dụng.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.IO;
using System.Collections.ObjectModel;

namespace Battleship.ShuggyCoUk
{
    public class Simple : IBattleshipOpponent
    {
        BoardView<OpponentsBoardState> opponentsBoard = new BoardView<OpponentsBoardState>(new Size(10,10));
        Rand rand = new Rand();
        int gridOddEven;
        Size size;

        public string Name { get { return "Simple"; } }

        public Version Version { get { return new Version(2, 1); }}

        public void NewMatch(string opponent) {}

        public void NewGame(System.Drawing.Size size, TimeSpan timeSpan)
        {
            this.size = size;
            this.opponentsBoard = new BoardView<OpponentsBoardState>(size);
            this.gridOddEven = rand.Pick(new[] { 0, 1 });
        }

        public void PlaceShips(System.Collections.ObjectModel.ReadOnlyCollection<Ship> ships)
        {
            BoardView<bool> board = new BoardView<bool>(size);
            var AllOrientations = new[] {
                ShipOrientation.Horizontal,
                ShipOrientation.Vertical };

            foreach (var ship in ships)
            {
                int avoidTouching = 3;
                while (!ship.IsPlaced)
                {
                    var l = rand.Pick(board.Select(c => c.Location));
                    var o = rand.Pick(AllOrientations);
                    if (ship.IsLegal(ships, size, l, o))
                    {
                        if (ship.IsTouching(ships, l, o)&& --avoidTouching > 0)
                            continue;
                        ship.Place(l, o);
                    }
                }
            }
        }
        protected virtual Point PickWhenNoTargets()
        {
            return rand.PickBias(x => x.Bias,
                opponentsBoard
                // nothing 1 in size
                .Where(c => (c.Location.X + c.Location.Y) % 2 == gridOddEven)
                .Where(c => c.Data == OpponentsBoardState.Unknown))
                .Location;
        }

        private int SumLine(Cell<OpponentsBoardState> c, int acc)
        {
            if (acc >= 0)
                return acc;
            if (c.Data == OpponentsBoardState.Hit)
                return acc - 1;
            return -acc;
        }

        public System.Drawing.Point GetShot()
        {
            var targets = opponentsBoard
                .Where(c => c.Data == OpponentsBoardState.Hit)
                .SelectMany(c => c.Neighbours())
                .Where(c => c.Data == OpponentsBoardState.Unknown)
                .ToList();
            if (targets.Count > 1)
            {
                var lines = targets.Where(
                    x => x.FoldAll(-1, SumLine).Select(r => Math.Abs(r) - 1).Max() > 1).ToList();
                if (lines.Count > 0)
                    targets = lines;
            }
            var target = targets.RandomOrDefault(rand);
            if (target == null)
                return PickWhenNoTargets();
            return target.Location;
        }

        public void OpponentShot(System.Drawing.Point shot)
        {
        }

        public void ShotHit(Point shot, bool sunk)
        {
            opponentsBoard[shot] = OpponentsBoardState.Hit;
            Debug(shot, sunk);
        }

        public void ShotMiss(Point shot)
        {
            opponentsBoard[shot] = OpponentsBoardState.Miss;
            Debug(shot, false);
        }

        public const bool DebugEnabled = false;

        public void Debug(Point shot, bool sunk)
        {
            if (!DebugEnabled)
                return;
            opponentsBoard.WriteAsGrid(
                Console.Out,
                x =>
                {
                    string t;
                    switch (x.Data)
                    {
                        case OpponentsBoardState.Unknown:
                            return " ";
                        case OpponentsBoardState.Miss:
                            t = "m";
                            break;
                        case OpponentsBoardState.MustBeEmpty:
                            t = "/";
                            break;
                        case OpponentsBoardState.Hit:
                            t = "x";
                            break;
                        default:
                            t = "?";
                            break;
                    }
                    if (x.Location == shot)
                        t = t.ToUpper();
                    return t;
                });
            if (sunk)
                Console.WriteLine("sunk!");
            Console.ReadLine();
        }

        public void GameWon()
        {
        }

        public void GameLost()
        {
        }

        public void MatchOver()
        {
        }

        #region Library code
        enum OpponentsBoardState
        {
            Unknown = 0,
            Miss,
            MustBeEmpty,
            Hit,
        }

        public enum Compass
        {
            North, East, South, West
        }

        class Cell<T>
        {
            private readonly BoardView<T> view;
            public readonly int X;
            public readonly int Y;
            public T Data;
            public double Bias { get; set; }

            public Cell(BoardView<T> view, int x, int y)
            {
                this.view = view; this.X = x; this.Y = y; this.Bias = 1.0;
            }

            public Point Location
            {
                get { return new Point(X, Y); }
            }

            public IEnumerable<U> FoldAll<U>(U acc, Func<Cell<T>, U, U> trip)
            {
                return new[] { Compass.North, Compass.East, Compass.South, Compass.West }
                    .Select(x => FoldLine(x, acc, trip));
            }

            public U FoldLine<U>(Compass direction, U acc, Func<Cell<T>, U, U> trip)
            {
                var cell = this;
                while (true)
                {
                    switch (direction)
                    {
                        case Compass.North:
                            cell = cell.North; break;
                        case Compass.East:
                            cell = cell.East; break;
                        case Compass.South:
                            cell = cell.South; break;
                        case Compass.West:
                            cell = cell.West; break;
                    }
                    if (cell == null)
                        return acc;
                    acc = trip(cell, acc);
                }
            }

            public Cell<T> North
            {
                get { return view.SafeLookup(X, Y - 1); }
            }

            public Cell<T> South
            {
                get { return view.SafeLookup(X, Y + 1); }
            }

            public Cell<T> East
            {
                get { return view.SafeLookup(X + 1, Y); }
            }

            public Cell<T> West
            {
                get { return view.SafeLookup(X - 1, Y); }
            }

            public IEnumerable<Cell<T>> Neighbours()
            {
                if (North != null)
                    yield return North;
                if (South != null)
                    yield return South;
                if (East != null)
                    yield return East;
                if (West != null)
                    yield return West;
            }
        }

        class BoardView<T> : IEnumerable<Cell<T>>
        {
            public readonly Size Size;
            private readonly int Columns;
            private readonly int Rows;

            private Cell<T>[] history;

            public BoardView(Size size)
            {
                this.Size = size;
                Columns = size.Width;
                Rows = size.Height;
                this.history = new Cell<T>[Columns * Rows];
                for (int y = 0; y < Rows; y++)
                {
                    for (int x = 0; x < Rows; x++)
                        history[x + y * Columns] = new Cell<T>(this, x, y);
                }
            }

            public T this[int x, int y]
            {
                get { return history[x + y * Columns].Data; }
                set { history[x + y * Columns].Data = value; }
            }

            public T this[Point p]
            {
                get { return history[SafeCalc(p.X, p.Y, true)].Data; }
                set { this.history[SafeCalc(p.X, p.Y, true)].Data = value; }
            }

            private int SafeCalc(int x, int y, bool throwIfIllegal)
            {
                if (x < 0 || y < 0 || x >= Columns || y >= Rows)
                {
                    if (throwIfIllegal)
                        throw new ArgumentOutOfRangeException("[" + x + "," + y + "]");
                    else
                        return -1;
                }
                return x + y * Columns;
            }

            public void Set(T data)
            {
                foreach (var cell in this.history)
                    cell.Data = data;
            }

            public Cell<T> SafeLookup(int x, int y)
            {
                int index = SafeCalc(x, y, false);
                if (index < 0)
                    return null;
                return history[index];
            }

            #region IEnumerable<Cell<T>> Members

            public IEnumerator<Cell<T>> GetEnumerator()
            {
                foreach (var cell in this.history)
                    yield return cell;
            }

            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
            {
                return this.GetEnumerator();
            }

            public BoardView<U> Transform<U>(Func<T, U> transform)
            {
                var result = new BoardView<U>(new Size(Columns, Rows));
                for (int y = 0; y < Rows; y++)
                {
                    for (int x = 0; x < Columns; x++)
                    {
                        result[x, y] = transform(this[x, y]);
                    }
                }
                return result;
            }

            public void WriteAsGrid(TextWriter w)
            {
                WriteAsGrid(w, "{0}");
            }

            public void WriteAsGrid(TextWriter w, string format)
            {
                WriteAsGrid(w, x => string.Format(format, x.Data));
            }

            public void WriteAsGrid(TextWriter w, Func<Cell<T>, string> perCell)
            {
                for (int y = 0; y < Rows; y++)
                {
                    for (int x = 0; x < Columns; x++)
                    {
                        if (x != 0)
                            w.Write(",");
                        w.Write(perCell(this.SafeLookup(x, y)));
                    }
                    w.WriteLine();
                }
            }

            #endregion
        }

        public class Rand
        {
            Random r;

            public Rand()
            {
                var rand = System.Security.Cryptography.RandomNumberGenerator.Create();
                byte[] b = new byte[4];
                rand.GetBytes(b);
                r = new Random(BitConverter.ToInt32(b, 0));
            }

            public int Next(int maxValue)
            {
                return r.Next(maxValue);
            }

            public double NextDouble(double maxValue)
            {
                return r.NextDouble() * maxValue;
            }

            public T Pick<T>(IEnumerable<T> things)
            {
                return things.ElementAt(Next(things.Count()));
            }

            public T PickBias<T>(Func<T, double> bias, IEnumerable<T> things)
            {
                double d = NextDouble(things.Sum(x => bias(x)));
                foreach (var x in things)
                {
                    if (d < bias(x))
                        return x;
                    d -= bias(x);
                }
                throw new InvalidOperationException("fell off the end!");
            }
        }
        #endregion
    }

    public static class Extensions
    {
        public static bool IsIn(this Point p, Size size)
        {
            return p.X >= 0 && p.Y >= 0 && p.X < size.Width && p.Y < size.Height;
        }

        public static bool IsLegal(this Ship ship,
            IEnumerable<Ship> ships,
            Size board,
            Point location,
            ShipOrientation direction)
        {
            var temp = new Ship(ship.Length);
            temp.Place(location, direction);
            if (!temp.GetAllLocations().All(p => p.IsIn(board)))
                return false;
            return ships.Where(s => s.IsPlaced).All(s => !s.ConflictsWith(temp));
        }

        public static bool IsTouching(this Point a, Point b)
        {
            return (a.X == b.X - 1 || a.X == b.X + 1) &&
                (a.Y == b.Y - 1 || a.Y == b.Y + 1);
        }

        public static bool IsTouching(this Ship ship,
            IEnumerable<Ship> ships,
            Point location,
            ShipOrientation direction)
        {
            var temp = new Ship(ship.Length);
            temp.Place(location, direction);
            var occupied = new HashSet<Point>(ships
                .Where(s => s.IsPlaced)
                .SelectMany(s => s.GetAllLocations()));
            if (temp.GetAllLocations().Any(p => occupied.Any(b => b.IsTouching(p))))
                return true;
            return false;
        }

        public static ReadOnlyCollection<Ship> MakeShips(params int[] lengths)
        {
            return new System.Collections.ObjectModel.ReadOnlyCollection<Ship>(
                lengths.Select(l => new Ship(l)).ToList());
        }

        public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Battleship.ShuggyCoUk.Simple.Rand rand)
        {
            T[] elements = source.ToArray();
            // Note i > 0 to avoid final pointless iteration
            for (int i = elements.Length - 1; i > 0; i--)
            {
                // Swap element "i" with a random earlier element it (or itself)
                int swapIndex = rand.Next(i + 1);
                T tmp = elements[i];
                elements[i] = elements[swapIndex];
                elements[swapIndex] = tmp;
            }
            // Lazily yield (avoiding aliasing issues etc)
            foreach (T element in elements)
            {
                yield return element;
            }
        }

        public static T RandomOrDefault<T>(this IEnumerable<T> things, Battleship.ShuggyCoUk.Simple.Rand rand)
        {
            int count = things.Count();
            if (count == 0)
                return default(T);
            return things.ElementAt(rand.Next(count));
        }
    }

}


5

Đây là về điều tốt nhất mà tôi có thể kết hợp trong thời gian rảnh rỗi, đó là về việc không tồn tại. Có một số chỉ số kiểm đếm trận đấu và trận đấu đang diễn ra, khi tôi thiết lập chức năng chính để lặp và liên tục chạy BattleshipCompetition cho đến khi tôi nhấn một phím.

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();
        }
    }
}

Logic này là lần gần nhất mà tôi phải đánh bại Dreadn think, chiến thắng khoảng 41% số trò chơi cá nhân. .


5

Máy tính của tôi đang được sửa chữa bởi dell ngay bây giờ, nhưng đây là nơi tôi đã ở vào tuần trước:

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; }
    }
}

2
Chúc mừng trên bạc. Bạn có nhớ mô tả thuật toán của bạn bằng từ ngữ? Nó sẽ rất thú vị để biết về.
Thomas Ahle

4

Nếu bạn thực sự buộc phải phân tích thì bạn có thể thấy các cơ chế của RandomOpponent được cung cấp rất kém hiệu quả. Nó cho phép chính nó chọn lại các vị trí đã nhắm mục tiêu và cho phép khung bắt buộc lặp lại cho đến khi nó chạm vào một vị trí mà nó chưa chạm vào hoặc thời gian cho mỗi lần di chuyển hết hạn.

Đối thủ này có hành vi tương tự (phân phối vị trí hiệu quả là như nhau) nó chỉ tự kiểm tra sự tỉnh táo và chỉ tiêu thụ một thế hệ số ngẫu nhiên trên mỗi cuộc gọi (khấu hao)).

Điều này sử dụng các lớp trong phần mở rộng / thư viện trả lời của tôi và tôi chỉ cung cấp các phương thức / trạng thái chính.

Shuffle được nâng lên từ câu trả lời của Jon Skeet ở đây

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

Tôi sẽ không thể tham gia, nhưng đây là thuật toán tôi sẽ thực hiện nếu có thời gian:

Đầu tiên, khi tôi phát hiện ra một cú đánh tôi không theo đuổi phần còn lại của con tàu ngay lập tức - tôi xây dựng một bảng các vị trí tàu và tìm hiểu xem liệu tôi đã đánh cả năm ít nhất một lần trước khi bắt đầu đánh chìm chúng hoàn toàn chưa. (Lưu ý rằng đây là một chính sách tồi cho biến thể bắn nhiều lần - xem bình luận)

  1. Nhấn vào trung tâm (xem ghi chú cuối cùng bên dưới - 'trung tâm' chỉ là sự thuận tiện để mô tả)
  2. Đánh vào vị trí 4 bên phải của trung tâm
  3. Nhấn điểm 1 xuống và một ở bên phải của trung tâm
  4. Lượt bốn điểm bên phải của cú đánh trước
  5. Tiếp tục theo mô hình đó (nên kết thúc bằng các đường chéo cách nhau bởi 3 khoảng trống lấp đầy bảng) Điều này sẽ đánh vào tất cả các thuyền dài 4 và 5, và một số lượng lớn thống kê gồm 3 và 2 thuyền.

  6. Bắt đầu đánh ngẫu nhiên các điểm nằm giữa các đường chéo, điều này sẽ bắt được những chiếc thuyền dài 2 và 3 chưa được chú ý.

Khi tôi đã phát hiện 5 lần truy cập, tôi sẽ xác định xem 5 lần truy cập đó có phải trên các thuyền riêng biệt hay không. Điều này tương đối dễ dàng bằng cách thực hiện thêm một vài bức ảnh gần các vị trí có hai cú đánh nằm trên cùng một đường ngang hoặc dọc và nằm trong 5 vị trí của nhau (có thể là hai cú đánh trên cùng một chiếc thuyền). Nếu chúng là thuyền riêng thì tiếp tục đánh chìm tất cả các tàu. Nếu chúng được tìm thấy là cùng một chiếc thuyền, hãy tiếp tục các mẫu điền ở trên cho đến khi tất cả 5 chiếc thuyền được định vị.

Thuật toán này là một thuật toán điền đơn giản. Các tính năng chính là nó không lãng phí thời gian chìm tàu ​​mà nó biết khi vẫn còn tàu mà nó không biết và nó không sử dụng mô hình làm đầy không hiệu quả (nghĩa là một mô hình hoàn toàn ngẫu nhiên sẽ lãng phí).

Ghi chú cuối cùng:

A) "Trung tâm" là điểm bắt đầu ngẫu nhiên trên bảng. Điều này giúp loại bỏ điểm yếu chính của thuật toán này. B) Mặc dù mô tả chỉ ra việc vẽ các đường chéo ngay từ đầu, lý tưởng nhất là thuật toán chỉ bắn vào các vị trí 'ngẫu nhiên' nằm dọc theo các đường chéo đó. Điều này giúp ngăn chặn đối thủ cạnh tranh thời gian bao lâu cho đến khi tàu của họ bị tấn công bởi các mẫu có thể dự đoán được.

Điều này mô tả thuật toán 'hoàn hảo' ở chỗ nó sẽ nhận được tất cả các tàu trong các bức ảnh dưới (9x9) / 2 + 10.

Tuy nhiên, nó có thể được cải thiện đáng kể:

Khi một con tàu bị bắn trúng, hãy xác định kích thước của nó trước khi thực hiện các đường chéo 'bên trong'. Bạn có thể đã tìm thấy 2 tàu, trong trường hợp đó các đường chéo bên trong có thể được đơn giản hóa để tìm tàu ​​3 kích thước nhanh hơn.

Xác định các giai đoạn trong trò chơi và hành động phù hợp. Thuật toán này có thể tốt đến một điểm nhất định trong trò chơi, nhưng các thuật toán khác có thể mang lại lợi ích tốt hơn như là một phần của trò chơi kết thúc. Ngoài ra, nếu người chơi khác rất gần đánh bại bạn, thuật toán khác có thể hoạt động tốt hơn - ví dụ thuật toán có rủi ro cao có thể thất bại thường xuyên hơn, nhưng khi nó hoạt động nhanh và bạn có thể đánh bại đối thủ gần chiến thắng hơn bạn .

Xác định phong cách chơi của đối thủ cạnh tranh - nó có thể cung cấp cho bạn manh mối về cách họ lên kế hoạch đặt tàu (nghĩa là rất có thể thuật toán của họ nhanh chóng xác định cách họ đặt tàu của họ - nếu công cụ duy nhất bạn có là búa, mọi thứ trông giống như một cái đinh)

-Adam


Chiến lược chờ tàu chìm cho đến khi tất cả được tìm thấy phụ thuộc rất nhiều vào biến thể một lần bắn mỗi lượt. Theo (số lượng tàu còn sống) - phiên bản mỗi lượt, việc thuận lợi là đánh chìm tàu ​​càng nhanh càng tốt để làm chậm đối thủ của bạn.
Jason Owen

4

Mục nhập của tôi.

Không có gì đặc biệt khủng khiếp, và tôi đã không có thời gian để thêm tất cả những ý tưởng hay mà tôi có.

Nhưng nó có vẻ chơi khá tốt. Chúng ta sẽ thấy nó cạnh tranh như thế nào:

(đặt cái này trong tập tin Missouri.csvà thêm vào dự án.)

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 (abelenky@alum.mit.edu)";
        private Size size;
        private List<HunterBoard> targetBoards;
        private ShotBoard shotBoard;
        private Stack<Attack> attackVector;
    }

    // An Attack is the data on the ship we are currently working on sinking.
    // It consists of a set of points, horizontal and vertical, from a central point.
    // And can be extended in any direction.
    public class Attack
    {
        public Attack(USSMissouri root, Point p)
        {
            Player = root;
            hit = p;
            horzExtent = new Extent(p.X, p.X);
            vertExtent = new Extent(p.Y, p.Y);
        }

        public Extent HorizontalExtent
        {
            get { return horzExtent; }
        }
        public Extent VerticalExtent
        {
            get { return vertExtent; }
        }
        public Point  FirstHit
        {
            get { return hit; }
        }

        public void AddHit(Point p)
        {
            if (hit.X == p.X) // New hit in the vertical direction
            {
                vertExtent.Min = Math.Min(vertExtent.Min, p.Y);
                vertExtent.Max = Math.Max(vertExtent.Max, p.Y);
            }
            else if (hit.Y == p.Y)
            {
                horzExtent.Min = Math.Min(horzExtent.Min, p.X);
                horzExtent.Max = Math.Max(horzExtent.Max, p.X);
            }
        }
        public Point[] GetNextTargets() 
        {
            List<Point> bors = new List<Point>();

            Point p;

            p = new Point(hit.X, vertExtent.Min-1);
            while (p.Y >= 0 && Player.theShotBoard[p] == Shot.Hit)
            {
                if (Player.theShotBoard[p] == Shot.Miss)
                {
                    break; // Don't add p to the List 'bors.  
                }
                --p.Y;
            }
            if (p.Y >= 0 && Player.theShotBoard[p] == Shot.None) // Add next-target only if there is no shot here yet.
            {
                bors.Add(p);
            }

            //-------------------

            p = new Point(hit.X, vertExtent.Max+1);
            while (p.Y < Player.theBoardSize.Height && Player.theShotBoard[p] == Shot.Hit)
            {
                if (Player.theShotBoard[p] == Shot.Miss)
                {
                    break; // Don't add p to the List 'bors.  
                }
                ++p.Y;
            }
            if (p.Y < Player.theBoardSize.Height && Player.theShotBoard[p] == Shot.None)
            {
                bors.Add(p);
            }

            //-------------------

            p = new Point(horzExtent.Min-1, hit.Y);
            while (p.X >= 0 && Player.theShotBoard[p] == Shot.Hit)
            {
                if (Player.theShotBoard[p] == Shot.Miss)
                {
                    break; // Don't add p to the List 'bors.  
                }
                --p.X;
            }
            if (p.X >= 0 && Player.theShotBoard[p] == Shot.None)
            {
                bors.Add(p);
            }

            //-------------------

            p = new Point(horzExtent.Max+1, hit.Y);
            while (p.X < Player.theBoardSize.Width && Player.theShotBoard[p] == Shot.Hit)
            {
                if (Player.theShotBoard[p] == Shot.Miss)
                {
                    break; // Don't add p to the List 'bors.  
                }
                ++p.X;
            }
            if (p.X < Player.theBoardSize.Width && Player.theShotBoard[p] == Shot.None)
            {
                bors.Add(p);
            }

            return bors.ToArray();
        }

        private Point hit; 
        private Extent horzExtent;
        private Extent vertExtent;
        private USSMissouri Player;
    }

    public struct Extent
    {
        public Extent(Int32 min, Int32 max)
        {
            Min = min;
            Max = max;
        }
        public Int32 Min;
        public Int32 Max;
    }

    public class Board  // The potential-Board, which measures the full potential of each square.
    {
        // A Board is the status of many things.
        public Board(Size boardsize)
        {
            size = boardsize;
            grid = new int[size.Width , size.Height];
            Array.Clear(grid,0,size.Width*size.Height);
        }

        public int this[int c,int r]
        {
            get { return grid[c,r];  }
            set { grid[c,r] = value; }
        }
        public int this[Point p]
        {
            get { return grid[p.X, p.Y];  }
            set { grid[p.X, p.Y] = value; }
        }

        public Point GetWeightedRandom(double r)
        {
            Int32 sum = 0;
            foreach(Int32 i in grid)
            {
                sum += i;
            }

            Int32 index = (Int32)(r*sum);

            Int32 x=0, y=0;
            for(y=0; y<size.Height; ++y)
            {
                for(x=0; x<size.Width; ++x)
                {
                    if (grid[x,y] == 0) continue; // Skip any zero-cells
                    index -= grid[x,y];
                    if (index < 0) break;
                }
                if (index < 0) break;
            }

            if (x == 10 || y == 10)
                throw new Exception("WTF");

            return new Point(x,y);
        }

        public Point GetBestShot()
        {
            int max=grid[0,0];
            for(int y=0; y<size.Height; ++y)
            {
                for (int x=0; x<size.Width; ++x)
                {
                    max = (grid[x,y] > max)? grid[x,y] : max;
                }
            }

            for(int y=0; y<size.Height; ++y)
            {
                for (int x=0; x<size.Width; ++x)
                {
                    if (grid[x,y] == max)
                    {
                        return new Point(x,y);
                    }
                }
            }
            return new Point(0,0);
        }

        public bool IsZero()
        {
            foreach(Int32 p in grid)
            {
                if (p > 0)
                {
                    return false;
                }
            }
            return true;
        }

        public override String ToString()
        {
            String output = "";
            String horzDiv = "   +----+----+----+----+----+----+----+----+----+----+\n";
            String disp;
            int x,y;

            output += "      A    B    C    D    E    F    G    H    I    J    \n" + horzDiv;

            for(y=0; y<size.Height; ++y)
            {
                output += String.Format("{0} ", y+1).PadLeft(3);
                for(x=0; x<size.Width; ++x)
                {
                    switch(grid[x,y])
                    {
                        case (int)Shot.None:       disp = "";  break;
                        case (int)Shot.Hit:        disp = "#"; break;
                        case (int)Shot.Miss:       disp = "."; break;
                        case (int)Shot.Unresolved: disp = "?"; break;
                        default:                   disp = "!"; break;
                    }

                    output += String.Format("| {0} ", disp.PadLeft(2));
                }
                output += "|\n" + horzDiv;
            }

            return output;
        }

        protected Int32[,] grid;
        protected Size     size;
    }

    public class HunterBoard
    {
        public HunterBoard(USSMissouri root, Size boardsize, Ship target)
        {
            size = boardsize;
            grid = new int[size.Width , size.Height];
            Array.Clear(grid,0,size.Width*size.Height);

            Player = root;
            Target = target;
            Initialize();
        }

        public void Initialize()
        {
            int x, y, i;

            for(y=0; y<size.Height; ++y)
            {
                for(x=0; x<size.Width - Target.Length+1; ++x)
                {
                    for(i=0; i<Target.Length; ++i)
                    {
                        grid[x+i,y]++;
                    }
                }
            }

            for(y=0; y<size.Height-Target.Length+1; ++y)
            {
                for(x=0; x<size.Width; ++x)
                {
                    for(i=0; i<Target.Length; ++i)
                    {
                        grid[x,y+i]++;
                    }
                }
            }
        }

        public int this[int c,int r]
        {
            get { return grid[c,r];  }
            set { grid[c,r] = value; }
        }
        public int this[Point p]
        {
            get { return grid[p.X, p.Y];  }
            set { grid[p.X, p.Y] = value; }
        }

        public void ShotMiss(Point p)
        {
            int x,y;
            int min, max;

            min = Math.Max(p.X-Target.Length+1, 0);
            max = Math.Min(p.X, size.Width-Target.Length);
            for(x=min; x<=max; ++x)
            {
                DecrementRow(p.Y, x, x+Target.Length-1);
            }

            min = Math.Max(p.Y-Target.Length+1, 0);
            max = Math.Min(p.Y, size.Height-Target.Length);
            for(y=min; y<=max; ++y)
            {
                DecrementColumn(p.X, y, y+Target.Length-1);
            } 

            grid[p.X, p.Y] = 0;
        }

        public void ShotHit(Point p)
        {
        }

        public override String ToString()
        {
            String output = String.Format("Target size is {0}\n", Target.Length);
            String horzDiv = "   +----+----+----+----+----+----+----+----+----+----+\n";
            int x,y;

            output += "      A    B    C    D    E    F    G    H    I    J    \n" + horzDiv;
            for(y=0; y<size.Height; ++y)
            {
                output += String.Format("{0} ", y+1).PadLeft(3);
                for(x=0; x<size.Width; ++x)
                {
                    output += String.Format("| {0} ", grid[x,y].ToString().PadLeft(2));
                }
                output += "|\n" + horzDiv;
            }
            return output;
        }

        // If we shoot at point P, how does that affect the potential of the board?
        public Int32 GetWeightAt(Point p)
        {
            int x,y;
            int potential = 0;
            int min, max;

            min = Math.Max(p.X-Target.Length+1, 0);
            max = Math.Min(p.X, size.Width-Target.Length);
            for(x=min; x<=max; ++x)
            {
                if (Player.theShotBoard.isMissInRow(p.Y, x, x+Target.Length-1) == false)
                {
                    ++potential;
                }
            }

            min = Math.Max(p.Y-Target.Length+1, 0);
            max = Math.Min(p.Y, size.Height-Target.Length);
            for(y=min; y<=max; ++y)
            {
                if (Player.theShotBoard.isMissInColumn(p.X, y, y+Target.Length-1) == false)
                {
                    ++potential;
                }
            } 

            return potential;
        }

        public void DecrementRow(int row, int rangeA, int rangeB)
        {
            int x;
            for(x=rangeA; x<=rangeB; ++x)
            {
                grid[x,row] = (grid[x,row]==0)? 0 : grid[x,row]-1;
            }
        }
        public void DecrementColumn(int col, int rangeA, int rangeB)
        {
            int y;
            for(y=rangeA; y<=rangeB; ++y)
            {
                grid[col,y] = (grid[col,y]==0)? 0 : grid[col,y]-1;
            }
        }

        private Ship Target = null;
        private USSMissouri Player;
        private Int32[,] grid;
        private Size     size;
    }

    public enum Shot
    {
        None = 0,
        Hit = 1,
        Miss = 2,
        Unresolved = 3
    };

    public class ShotBoard
    {
        public ShotBoard(Size boardsize)
        {
            size = boardsize;
            grid = new Shot[size.Width , size.Height];

            for(int y=0; y<size.Height; ++y)
            {
                for(int x=0; x<size.Width; ++x)
                {
                    grid[x,y] = Shot.None;
                }
            }
        }

        public Shot this[int c,int r]
        {
            get { return grid[c,r];  }
            set { grid[c,r] = value; }
        }
        public Shot this[Point p]
        {
            get { return grid[p.X, p.Y];  }
            set { grid[p.X, p.Y] = value; }
        }

        public override String ToString()
        {
            String output = "";
            String horzDiv = "   +----+----+----+----+----+----+----+----+----+----+\n";
            String disp;
            int x,y;

            output += "      A    B    C    D    E    F    G    H    I    J    \n" + horzDiv;

            for(y=0; y<size.Height; ++y)
            {
                output += String.Format("{0} ", y+1).PadLeft(3);
                for(x=0; x<size.Width; ++x)
                {
                    switch(grid[x,y])
                    {
                        case Shot.None:       disp = "";  break;
                        case Shot.Hit:        disp = "#"; break;
                        case Shot.Miss:       disp = "."; break;
                        case Shot.Unresolved: disp = "?"; break;
                        default:              disp = "!"; break;
                    }

                    output += String.Format("| {0} ", disp.PadLeft(2));
                }
                output += "|\n" + horzDiv;
            }
            return output;
        }

        // Functions to find shots on the board, at a specific point, or in a row or column, within a range
        public bool ShotAt(Point p)
        {
            return !(this[p]==Shot.None);
        }
        public bool isMissInColumn(int col, int rangeA, int rangeB)
        {
            for(int y=rangeA; y<=rangeB; ++y)
            {
                if (grid[col,y] == Shot.Miss)
                {
                    return true;
                }
            }
            return false;
        }
        public bool isMissInRow(int row, int rangeA, int rangeB)
        {
            for(int x=rangeA; x<=rangeB; ++x)
            {
                if (grid[x,row] == Shot.Miss)
                {
                    return true;
                }
            }
            return false;
        }
        protected Shot[,] grid;
        protected Size     size;
    }
}

Và bây giờ tôi đã gửi bài dự thi của mình, một số thống kê sơ bộ: so với chiến thắng 44% của BP7. / so với Dreadn think 20% chiến thắng. / so với Farnsworth thắng 42%. Nó quả thật là một dự án thú vị.
abelenky

2

Đây không phải là minimax. Trên thực tế sau khi đặt tàu, mỗi người chơi không thể tự chơi, dẫn đến một số lượt mà anh ta phải đánh chìm mọi tàu đối phương? Người nào mất ít lượt hơn sẽ thắng.

Tôi không nghĩ rằng có bất kỳ chiến lược chung nào tốt ngoài việc đánh chìm các tàu bị tấn công và cố gắng giảm thiểu số lượng phát bắn để che những nơi còn lại có thể mà tàu có thể ẩn nấp.

Tất nhiên, có thể có các chiến lược đối kháng cho bất cứ điều gì không ngẫu nhiên. Nhưng tôi không nghĩ rằng có những chiến lược tốt đối với tất cả những người chơi có thể.


1
Có khả năng, vâng, họ có thể tự chơi. Đó không phải là cách nó sẽ được chạy. Ý tưởng tuyệt vời, mặc dù. Trong cuộc thi này, tôi muốn có thể tránh được những cú đánh của đối thủ.
John Gietzen

2
Tôi hiểu rồi. Sử dụng dữ liệu từ các trò chơi trước với cùng một đối thủ, người ta có thể thích nghi với anh ta không?
ziggystar

2

Trên thực tế, tôi nghĩ vấn đề lớn nhất với câu đố là về cơ bản là hai động tác. Một động tác là đặt tàu của bạn, hành động khác là tìm tàu ​​địch (tuy nhiên phân đoạn đó có thể là phần thứ hai, ngoài việc cố gắng đánh bại đồng hồ với một yếu tố ngẫu nhiên, đó chỉ là 'chạy thuật toán của bạn'). Không có cơ chế để cố gắng xác định và sau đó chống lại một chiến lược của kẻ thù, đó là điều làm cho các cuộc thi tương tự dựa trên các vòng liên tiếp của "kéo giấy đá" khá thú vị.

Ngoài ra, tôi nghĩ sẽ tuyệt hơn nếu bạn chỉ định trò chơi là giao thức mạng và sau đó cung cấp khung để thực hiện giao thức đó trong C #, thay vì chỉ ra rằng tất cả các giải pháp nên là C #, nhưng đó chỉ là ý kiến ​​của tôi.

EDIT: Tôi hủy bỏ quan điểm ban đầu của mình, vì tôi đã không đọc kỹ các quy tắc cạnh tranh.


Không phải tất cả các giải pháp phải có trong C #. Tôi có thể biên dịch và liên kết trong một hội đồng riêng biệt. Ngoài ra, bạn sẽ có thể thống kê đối thủ của bạn.
John Gietzen

J #? có lẽ? Lol JK. Tôi có một khung TCP cho việc này, nhưng giải đấu này cần phải chạy rất nhanh.
John Gietzen

Tại sao bạn cho rằng giao tiếp TCP giữa hai quy trình trên cùng một máy sẽ không nhanh như vậy?
Jherico

@Jherico: Nếu tôi đang sử dụng TCP, tôi sẽ cách ly các động cơ trên PC của chính họ để họ có thể sử dụng bất kỳ tài nguyên CPU nào họ muốn.
John Gietzen

Mặc dù vậy, hai máy trên cùng một làn có thể dễ dàng hoàn thành trò chơi trong một giây với chi phí mạng tối thiểu
Jherico

2

Tôi luôn thích bắt đầu ở giữa và xoắn ốc từ một điểm đó để lại không quá 1 khoảng trống giữa bất kỳ điểm nào khác để giải thích cho vị thần chết tiệt đó ... không gian giữa các phát bắn phụ thuộc vào việc tàu bị chìm. nếu tàu B là lần cuối, các cảnh quay chỉ phải chừa lại 4 khoảng trống ở giữa để giảm thiểu các cú đánh lãng phí


1
Vậy ... tôi chỉ cần tránh xa giữa chừng? :)
darron

14
Bạn cũng cần tránh xa các cạnh, bởi vì một cú đánh cạnh chứa nhiều thông tin cho đối thủ của bạn hơn là cú đánh không biên. Vì vậy, bạn nên đặt tất cả các tàu của bạn trong một khu vực không trung, không cạnh. Trừ khi đó là những gì họ mong đợi bạn làm.
Jherico

1
Nếu bạn bắt đầu bằng cách để lại 3 hoặc 4 khoảng trắng, bạn có thể đủ may mắn để đạt được mục tiêu phụ. Nếu không, hãy quay lại và thử điền vào các khoảng trống. Chi tiết tại: somethinkodd.com/oddthinking/2009/10/29/battleship-strategy
Oddthinking

18
Con tàu có hai lỗ không phải là một con tàu chết tiệt , đó là một chiếc thuyền PT chết tiệt . Các phụ có ba lỗ. :)
quạ

2

Có một cuộc thi tương tự được tiến hành bởi Tiến sĩ James Heather của Đại học Surrey thay mặt cho Hiệp hội Máy tính Anh.

Các giới hạn được đặt trên các tài nguyên - cụ thể là thời gian xử lý tối đa trên mỗi lượt, không có trạng thái nào có thể được lưu trữ giữa các lần di chuyển, kích thước heap tối đa được áp đặt. Để giới hạn thời gian, AI có thể gửi di chuyển tại bất kỳ điểm nào trong khung thời gian và sẽ được yêu cầu di chuyển khi chấm dứt lượt.

Rất thú vị - xem thêm tại: http://www.bcsstudentcontest.com/

Có thể cho bạn thêm một số ý tưởng.


2

Như vậy, giải pháp mở và chạy mà không cần sửa đổi trong phát triển đơn âm trong linux 9.10 linux


1

Bạn đã viết:

  • Bất cứ điều gì được coi là trái với tinh thần của cuộc thi sẽ là căn cứ để bị loại.
  • Can thiệp vào một đối thủ là trái với tinh thần của cuộc thi.

vui lòng xác định "chống lại tinh thần của cuộc thi" và "can thiệp vào đối thủ"?

Ngoài ra - để đơn giản hóa, tôi khuyên bạn:

  • không cho phép sử dụng CPU trong suốt khe cắm CPU của đối thủ.
  • không cho phép xử lý song song luồng và thay vào đó cho nhiều giây CPU hơn trên một luồng. Điều này sẽ đơn giản hóa việc lập trình AI và sẽ không làm tổn thương bất cứ ai bị ràng buộc CPU / bộ nhớ.

PS - một câu hỏi cho các tài liệu hậu kỳ CS ẩn giấu ở đây: không phải trò chơi này có thể giải quyết được (tức là có một chiến lược duy nhất, tốt nhất không?). vâng, kích thước bảng và số bước làm cho minimax et al bắt buộc, nhưng tôi vẫn phải tự hỏi ... nó khác xa với cờ vây và cờ vua rất phức tạp.


Tôi đã suy nghĩ trong đầu khi tôi nói "Can thiệp". Tôi không muốn các đối thủ chiến thắng vì họ đã xoay một động cơ khác đến chết.
John Gietzen

8
Tôi muốn đề xuất rằng gián điệp là một phần quan trọng của chiến tranh hiện đại, vì vậy phản ánh để tìm ra các mục tiêu sẽ là lý tưởng - sau tất cả, đó là một trong những phương pháp được sử dụng trong chiến tranh thế giới thứ hai ...
Rowland Shaw

Tôi có một khung để cô lập các động cơ trên các PC khác nhau, giao tiếp qua TCP / IP, khiến Reflection trở nên vô dụng. Tuy nhiên, do số lượng bài dự tính của tôi, Điều này sẽ khiến cuộc thi mất nhiều thời gian.
John Gietzen

6
Tôi không biết họ đã có Reflection hồi đó!
Markus Nigbur

1

Tôi dự đoán rằng người quản lý đảo ngược kỹ thuật hạt giống và cuộc gọi ngẫu nhiên của đối thủ sẽ giành chiến thắng.

Không chắc khả năng đó là như thế nào.


Các đối thủ có tùy chọn sử dụng CSPRNG.
John Gietzen

Điểm tốt, mặc dù tôi thừa nhận rằng kỹ thuật đảo ngược như vậy là vượt quá chuyên môn của tôi. Tôi đoán rằng khía cạnh dễ bị tổn thương nhất sẽ là thuật toán lựa chọn mô hình lửa - nhưng ngay cả khi đó bạn sẽ không nhất thiết thu được nhiều từ việc phá vỡ nó, vì không có cách nào bạn có thể di chuyển tàu của mình khi trò chơi bắt đầu.
Triston Attridge

Khi tôi nộp đơn xin thực tập nghiên cứu, chúng tôi đã viết các chương trình tàu chiến và thi đấu. Bằng cách đặt hạt giống ngẫu nhiên chính xác là cách tôi giành được X)
P Shved

1
Giả sử thuật toán vị trí tàu đơn giản hợp lý, tôi sẽ tưởng tượng người ta có thể, sau khi nhận được một vài cú đánh vào các tàu khác nhau, bắt đầu sử dụng hầu hết các lượt đi vòng qua tất cả các hạt ngẫu nhiên có thể (có thể bắt đầu từ đâu đó gần thời điểm hiện tại và di chuyển về phía trước / lùi một bước hoặc lâu hơn) và xem cái nào tạo ra các vị trí tàu tương thích với các lần truy cập được quan sát.
Domenic

1

Có lẽ, cũng có thể, có thể chạy một loạt trong số này với các biến thể trong trò chơi.

Thêm vào những thứ như máy bay 3d hoặc có thể di chuyển một con tàu thay vì bắn trong một lượt có lẽ sẽ thay đổi trò chơi một chút công bằng.


2
Có biến thể "salvo". Nơi bạn có thể bắn bao nhiêu phát mỗi lượt khi bạn còn tàu.
John Gietzen

Một biến thể thú vị là tốt. Tôi dường như nhớ lại việc chơi một phiên bản máy tính cũng có một chiếc máy bay. Nó sẽ bắn ngẫu nhiên vào các vị trí trên bảng đối diện.
Glenn

một biến thể khác: là kích thước của bảng + số lượng tàu.
Nga

1

Tổng thời gian trò chơi là một giây cụ thể. Các hoạt động CPU có giá trị thứ hai sẽ khác với máy của tôi so với máy thi đấu. Nếu tôi tối ưu hóa thuật toán Battle Ship để sử dụng nhiều thời gian CPU nhất trong vòng 1 giây, thì nó được chạy trên một máy giải đấu chậm hơn có thể, nó sẽ luôn thua.

Tôi không chắc chắn làm thế nào để vượt qua giới hạn này của khung, nhưng nó nên được giải quyết.

...

Một ý tưởng là làm những gì đã được thực hiện trong cuộc thi này http://www.bcsstudentcontest.com /

Và có thời gian tối đa trên mỗi lượt chơi so với tổng thời gian trò chơi tối đa. Bằng cách này, tôi có thể giới hạn các thuật toán để phù hợp với thời gian bật. Một trò chơi có thể kéo dài hơn 50 đến 600 lượt, nếu thuật toán của tôi quản lý tổng thời gian trò chơi của nó, nó có thể không cho đủ thời gian để thực hiện công việc tốt nhất của mình hoặc nó có thể mất quá nhiều thời gian và mất. Rất khó để quản lý tổng thời gian trò chơi trong thuật toán Battleship.

Tôi sẽ đề nghị thay đổi các quy tắc để giới hạn thời gian lần lượt chứ không phải tổng thời gian trò chơi.

Biên tập

Nếu tôi viết một thuật toán liệt kê tất cả các bức ảnh có thể và sau đó xếp hạng chúng, thì sẽ có bức ảnh xếp hạng cao nhất. Sẽ mất quá nhiều thời gian để tạo ra tất cả các bức ảnh có thể, vì vậy tôi sẽ để thuật toán chạy trong một khoảng thời gian nhất định sau đó dừng nó.

Nếu có giới hạn theo lượt, tôi có thể để thuật toán chạy trong 0,9 giây và trả lại cú đánh thứ hạng cao nhất, và cũng có thể vượt qua giới hạn thời gian rẽ.

Nếu tôi bị giới hạn trong tổng thời gian trò chơi trong một giây, sẽ rất khó để xác định thuật toán sẽ chạy trong bao lâu cho mỗi lượt. Tôi sẽ muốn tối đa thời gian CPU của tôi. Nếu một trò chơi kéo dài 500 vòng, tôi có thể giới hạn mỗi lượt chơi là 0,002 giây, nhưng nếu một trò chơi kéo dài 100 vòng, tôi có thể cho mỗi lượt chơi 0,01 giây thời gian CPU.

Sẽ là không thực tế khi một thuật toán sử dụng tìm kiếm bán toàn bộ không gian bắn để tìm ra bức ảnh tốt nhất với giới hạn hiện tại.

Tổng thời gian trò chơi là 1 giây đang giới hạn loại thuật toán có thể được sử dụng hiệu quả để cạnh tranh trong trò chơi.


Điều này sẽ được chạy trên lõi tứ Intel Q9550SX, ram 8 GB, máy Vista 64. Là 1 giây sẽ là một yếu tố hạn chế?
John Gietzen

Tôi đoán bạn nên biến chiến hạm AI của mình thành đa luồng, sau đó, để tính số lần bắn tối đa cho mỗi khoảng thời gian đó.
Jeff Atwood

Bí quyết là làm thế nào để giới hạn khoảng thời gian rẽ. Nếu tôi giới hạn 0,00005 giây, tôi an toàn không chạy quá giới hạn thời gian, nhưng tôi hạn chế đáng kể không gian tìm kiếm. Nếu tôi tăng giới hạn thời gian bật, không gian tìm kiếm sẽ tăng nhưng tôi có nguy cơ hết thời gian.
TonyAbell

@TonyAbell: Nếu điều quan trọng là có giới hạn thời gian theo lượt, tại sao không bắt đầu với giá trị ban đầu và sau đó điều chỉnh nó từ vòng này sang vòng khác? Sau khoảng một nửa vòng, rất có thể bạn sẽ tìm thấy độ dài lượt tối ưu cho đối thủ mà bạn phải đối mặt.
kyokley

Bạn nên theo dõi thời gian còn lại của mình và giới hạn ở mức 1/2 thời gian còn lại.
John Gietzen

1

Tôi đang xử lý ở đây bằng cách không đưa mã thực tế vào - nhưng tôi sẽ gặp phải một số quan sát chung:

  • Vì tất cả các tàu đều có kích thước tối thiểu 2 ô, bạn có thể sử dụng tối ưu hóa mà tôi đã thấy khi triển khai trò chơi trong Space Quest V - chỉ bắn vào các ô thay thế trong mô hình kim cương trong khi nó đang "tìm kiếm" mục tiêu. Điều này giúp loại bỏ một nửa hình vuông, trong khi vẫn đảm bảo rằng bạn sẽ tìm thấy tất cả các con tàu cuối cùng.
  • Một mô hình bắn ngẫu nhiên khi tìm kiếm mục tiêu sẽ thống kê mang lại kết quả tốt nhất qua nhiều trò chơi.

1

! [Mật độ xác suất] [1] nhập mô tả hình ảnh của cô ấy

! [nhập mô tả hình ảnh ở đây] [2]

Tôi đã thử nghiệm so sánh kết quả bắn randon với săn bắn / mục tiêu câm và cuối cùng là một tìm kiếm tinh vi.

Giải pháp tốt nhất dường như là tạo ra một hàm mật độ xác suất cho khả năng bất kỳ hình vuông riêng lẻ nào được sử dụng bởi các tàu còn lại và nhắm đến hình vuông có giá trị cao nhất.

Bạn có thể thấy kết quả của tôi ở đây nhập mô tả liên kết ở đây


Có lẽ bạn có thể sửa câu trả lời của bạn và đặc biệt là hình ảnh và liên kết của bạn?
Bart

-2

"Chiến hạm" là một vấn đề NP-Complete khoa học máy tính cổ điển.

http://en.wikipedia.org/wiki/List_of_NP-complete_probols

(tìm Battleship - nó ở đó, dưới các trò chơi và câu đố)


4
Đó là một câu đố chiến hạm ( en.wikipedia.org/wiki/Battleship_(puheads) ), không phải Battleship trò chơi ( en.wikipedia.org/wiki/Battleship_(game) ).
Jason Berkan

Vâng, như Jason đã nói đây là một động vật hoàn toàn khác.
John Gietzen

3
Hehehe. Nhiệm vụ tiếp theo tôi nhận được trong công việc Tôi sẽ nói đó là NP-hoàn thành, sau đó ăn trưa dài. :-)
Bork Blatt
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.