सर्वश्रेष्ठ बैटलशिप AI क्या है?


315

युद्धपोत!

2003 में वापस (जब मैं 17 साल का था), मैंने एक बैटलशिप एआई कोडिंग प्रतियोगिता में भाग लिया । भले ही मैंने वह टूर्नामेंट गंवा दिया, लेकिन मैंने बहुत मज़ा किया और इससे बहुत कुछ सीखा।

अब, मैं सर्वश्रेष्ठ युद्धपोत एआई की खोज में, इस प्रतियोगिता को फिर से जीवित करना चाहूंगा।

यहाँ अब फ्रेमवर्क है, जिसे Bitbucket पर होस्ट किया गया है

विजेता को +450 प्रतिष्ठा दी जाएगी! प्रतियोगिता 17 नवंबर, 2009 को शुरू होगी । 17 तारीख को शून्य-घंटे की तुलना में बाद में कोई प्रविष्टि या संपादन स्वीकार नहीं किया जाएगा। (केंद्रीय मानक समय) अपनी प्रविष्टियाँ जल्दी भेजें, ताकि आप अपना अवसर न छोड़ें!

इस उद्देश्य को रखने के लिए , कृपया प्रतियोगिता की भावना का पालन करें।

खेल के नियम:

  1. खेल 10x10 ग्रिड पर खेला जाता है।
  2. प्रत्येक प्रतियोगी अपने ग्रिड पर 5 जहाजों (लंबाई 2, 3, 3, 4, 5) में से प्रत्येक को रखेगा।
  3. कोई जहाज ओवरलैप नहीं हो सकता है, लेकिन वे आसन्न हो सकते हैं।
  4. प्रतियोगी तब अपने प्रतिद्वंद्वी पर एकल शॉट फायरिंग करते हैं।
    • खेल में भिन्नता प्रत्येक जीवित जहाज के लिए प्रति वॉली में कई शॉट फायरिंग की अनुमति देती है।
  5. यदि शॉट डूबता है, हिट करता है, या याद करता है, तो प्रतिद्वंद्वी प्रतिद्वंद्वी को सूचित करेगा।
  6. जब किसी एक खिलाड़ी के सभी जहाज डूब जाते हैं, तो खेल खेलना समाप्त हो जाता है।

प्रतियोगिता के नियम:

  1. प्रतियोगिता की भावना सर्वश्रेष्ठ बैटलशिप एल्गोरिथ्म को खोजने के लिए है।
  2. प्रतियोगिता की भावना के खिलाफ जो भी समझा जाता है, वह अयोग्यता के लिए आधार होगा।
  3. एक प्रतिद्वंद्वी के साथ हस्तक्षेप करना प्रतियोगिता की भावना के खिलाफ है।
  4. मल्टीथ्रेडिंग का उपयोग निम्नलिखित प्रतिबंधों के तहत किया जा सकता है:
    • एक से अधिक धागे नहीं चल सकते हैं जबकि यह आपकी बारी नहीं है। (हालांकि, थ्रेड्स की कोई भी संख्या "निलंबित" स्थिति में हो सकती है)।
    • कोई भी धागा "सामान्य" के अलावा किसी प्राथमिकता पर नहीं चल सकता है।
    • उपरोक्त दो प्रतिबंधों को देखते हुए, आपको अपनी बारी के दौरान कम से कम 3 समर्पित सीपीयू कोर की गारंटी दी जाएगी।
  5. खेल के प्रति सीपीयू समय की 1 सेकंड की सीमा को प्राथमिक धागे पर प्रत्येक प्रतियोगी को आवंटित किया जाता है।
  6. वर्तमान गेम को खोने के परिणामस्वरूप समय समाप्त हो जाता है।
  7. वर्तमान खेल को खोने के परिणामस्वरूप कोई भी अपवाद नहीं होगा।
  8. नेटवर्क एक्सेस और डिस्क एक्सेस की अनुमति है, लेकिन आपको समय प्रतिबंध काफी निषेधात्मक लग सकता है। हालांकि, टाइम स्ट्रेन को कम करने के लिए कुछ सेट-अप और टियर-डाउन तरीके जोड़े गए हैं।
  9. कोड को एक जवाब के रूप में स्टैक ओवरफ्लो पर पोस्ट किया जाना चाहिए, या, यदि बहुत बड़ा, जुड़ा हुआ है।
  10. एक प्रविष्टि का अधिकतम कुल आकार (संयुक्त राष्ट्र संकुचित) 1 एमबी है।
  11. आधिकारिक तौर पर, .Net 2.0 / 3.5 केवल फ्रेमवर्क की आवश्यकता है।
  12. आपकी प्रविष्टि को IBattleshipOpponent इंटरफ़ेस को लागू करना चाहिए।

स्कोरिंग:

  1. 101 मैचों में से सर्वश्रेष्ठ 51 गेम एक मैच के विजेता हैं।
  2. सभी प्रतियोगी एक-दूसरे के खिलाफ गोल-गोल, रॉबिन स्टाइल में मैच खेलेंगे।
  3. प्रतियोगियों का सबसे अच्छा आधा विजेता को निर्धारित करने के लिए एक डबल-उन्मूलन टूर्नामेंट खेलेंगे। (दो की सबसे छोटी शक्ति जो वास्तव में आधे से अधिक या बराबर है।)
  4. मैं टूर्नामेंट के लिए टूर्नामेंट टूर्नामेंट का उपयोग करूंगा ।
  5. परिणाम यहां पोस्ट किए जाएंगे।
  6. यदि आप एक से अधिक प्रविष्टि जमा करते हैं, तो केवल आपकी सर्वश्रेष्ठ स्कोरिंग प्रविष्टि डबल-एलिम के लिए पात्र है।

सौभाग्य! मज़े करो!


EDIT 1: फ्रीड का
धन्यवाद , जिसने फ़ंक्शन में त्रुटि पाई है। इसे ठीक कर लिया गया है। कृपया फ्रेमवर्क का अद्यतन संस्करण डाउनलोड करें।Ship.IsValid

EDIT 2:
चूँकि आँकड़ों को डिस्क में बनाए रखने में महत्वपूर्ण रुचि रही है और इसलिए, मैंने कुछ गैर-समयबद्ध सेट-अप और आंसू-डाउन घटनाओं को जोड़ा है जो आवश्यक कार्यक्षमता प्रदान करनी चाहिए। यह एक अर्ध विराम परिवर्तन है । यह कहना है: इंटरफ़ेस को फ़ंक्शंस जोड़ने के लिए संशोधित किया गया है, लेकिन उनके लिए कोई निकाय आवश्यक नहीं है। कृपया फ्रेमवर्क का अद्यतन संस्करण डाउनलोड करें।

संपादित करें 3:
बग फिक्स 1: GameWonऔर GameLostकेवल टाइम आउट के मामले में बुलाया जा रहा था।
बग फिक्स 2: यदि कोई इंजन हर खेल को समाप्त कर रहा था, तो प्रतियोगिता कभी समाप्त नहीं होगी।
कृपया फ्रेमवर्क का अद्यतन संस्करण डाउनलोड करें।

संस्करण 4:
टूर्नामेंट के परिणाम:


यदि प्रविष्टि के लिए बड़े डेटाबेस की आवश्यकता होती है, तो क्या यह नेट से जुड़ सकता है? अर्थात। क्या प्रविष्टि वेब सेवा कॉल कर सकती है?
रेमुस रुसानु

प्रविष्टियों पर एक आकार सीमा है?
झिरिको

8
@ सीन: इसके अलावा, मैंने जेफ एटवुड से सलाह ली कि यह उचित है या नहीं। यहाँ उनकी प्रतिक्रिया है: twitter.com/codinghorror/status/5203185621
जॉन गीत्जन

1
इसके अलावा, मैं taht जोड़ूंगा, इन 50 खेलों के लिए अपरिहार्य यादृच्छिक घटक को देखते हुए बहुत अच्छे कार्यान्वयन के बीच सटीक अंतर करने के लिए पर्याप्त नहीं होगा। मुझे लगता है कि उचित दृष्टिकोण के लिए 501 या अधिक आवश्यक हो सकता है, जो बेहतर है।
शुग्गीकोउक

1
एक "शांतिपूर्ण" प्रतिद्वंद्वी जो जहाजों को रखने से इनकार करता है, प्रतियोगिता को लटका देता है। यकीन नहीं होता कि आप इस तरह से मूर्खतापूर्ण चीजें करने वाले लोगों की कितनी परवाह करते हैं। :)
जो

जवाबों:


56

मैं प्रति मैच बहुत अधिक खेल करने के लिए दूसरी गति देता हूं। 50 गेम करना सिर्फ एक सिक्का उछालना है। परीक्षण एल्गोरिदम के बीच किसी भी उचित अंतर को प्राप्त करने के लिए मुझे 1000 गेम करने की आवश्यकता थी।

डाउनलोड Dreadnought 1.2

रणनीतियाँ:

  • > 0 हिट वाले जहाजों के लिए सभी संभावित पदों पर नज़र रखें। यह सूची ~ 30K से कभी बड़ी नहीं होती है, इसलिए इसे सभी जहाजों के लिए सभी संभावित पदों की सूची के विपरीत बिल्कुल रखा जा सकता है (जो बहुत बड़ा है)।

  • गेटशॉट एल्गोरिथ्म के दो भाग हैं, एक जो यादृच्छिक शॉट्स उत्पन्न करता है और दूसरा जो पहले से ही हिट जहाज डूबने को समाप्त करने की कोशिश करता है। यदि कोई संभावित स्थिति (ऊपर दी गई सूची से) है जिसमें सभी हिट जहाज डूब गए हैं, तो हम यादृच्छिक शॉट्स करते हैं। अन्यथा, हम शूट करने के लिए एक स्थान को उठाकर एक जहाज को खत्म करने की कोशिश करते हैं, जिस पर सबसे संभव स्थिति (भारित) को समाप्त करता है।

  • यादृच्छिक शॉट्स के लिए, स्थान को ओवरलैप करने वाले एक बेकार जहाज की संभावना के आधार पर शूट करने के लिए सर्वश्रेष्ठ स्थान की गणना करें।

  • अनुकूली एल्गोरिथ्म जो उन स्थानों पर जहाजों को रखता है जहां प्रतिद्वंद्वी को शूट करने के लिए सांख्यिकीय रूप से कम है।

  • अनुकूली एल्गोरिथ्म जो उन स्थानों पर शूट करना पसंद करता है जहां प्रतिद्वंद्वी अपने जहाजों को रखने के लिए सांख्यिकीय रूप से अधिक संभावना है।

  • जगह जहाज ज्यादातर एक दूसरे को नहीं छूते हैं।


मेरी परीक्षण मशीन (एक ULV सेलेरॉन नेटबुक) पर यह कोड लगातार समय समाप्त हो जाता है। जब मैं इसे हर समय लेने देता हूं, तो यह सरल (लगभग 90% सफलता दर) कोड़ा मारता है। यदि आप मशीन की कल्पना पर बहुत भरोसा कर रहे हैं, तो आप पर चलने के लिए आप समय पर हिट करने के लिए आप अपने आप को कुछ wiggle कमरा देना चाहते हो सकता है ...
ShuggyCoUk

दिलचस्प ... यह टूर्नामेंट मशीन पर ठीक चलता है। हालांकि, एक "संपूर्ण" इंजन हालांकि उस समय के लिए अनुकूल होगा जब उसने पहले ही खर्च कर दिया था।
जॉन गिएट्ज़ेन

35

यहाँ मेरा प्रवेश है! (सबसे भोला समाधान संभव)

"रैंडम 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
वास्तव में, यह उत्तर अच्छा है क्योंकि यह बहुत ही संक्षिप्त रूप में दिखाता है कि एपीआई को आपको प्रतिस्पर्धा करने के लिए लागू करना होगा ... :)
dicroce

1
जब मैंने अपने कॉलेज के एल्गोरिथम वर्ग में एक समान परियोजना का निर्माण किया तो मैंने कुछ निर्णय लेने के साथ यादृच्छिक तर्क का उपयोग किया। यह कभी-कभी अच्छा होता था!
नातान टेलर

2
यह अतिव्यापी जहाजों को जगह देने का प्रयास नहीं कर सकता था?

6
हां, लेकिन इंजन इसे खारिज कर देगा। यह तब एआई को उन्हें फिर से रखने के लिए कहेगा, लेकिन इस बार, सख्त आवाज के साथ। (द्वारा देखा गया pop ax \ cmp ax, 1 \ je stern)
जॉन गीत्ज़ेन २

5
महत्वपूर्ण नोट foranyone, जो मुझे पसंद करते हैं, लगा कि वे पहले से रखे गए शॉट्स को याद करके और दोहराते हुए आसानी से इसे हरा सकते हैं। ढांचा दोहराव को नजरअंदाज करेगा और आपको एक और मौका देगा जब तक कि आपका कुल समय सीमा से कम हो। यह मेरी राय में खराब है, अगर कोई अपने
अहंकार को

22

यहाँ लोगों के खिलाफ खेलने के लिए एक प्रतिद्वंद्वी है:

एक निश्चित ज्यामिति-प्रेरित रणनीति का उपयोग करने के बजाय, मुझे लगा कि अंतर्निहित संभावनाओं का अनुमान लगाने का प्रयास करना दिलचस्प होगा कि कोई विशेष अस्पष्ट स्थान एक जहाज रखता है।

इस अधिकार को करने के लिए, आप उन जहाजों के सभी संभावित विन्यासों का पता लगाएंगे जो दुनिया के आपके वर्तमान दृष्टिकोण को फिट करते हैं, और फिर उन कॉन्फ़िगरेशन के आधार पर संभावनाओं की गणना करते हैं। आप इसके बारे में सोच सकते हैं जैसे एक पेड़ की खोज:

संभावित युद्धपोत का विस्तार http://natekohl.net/media/battleship-tree.png बताता है

उस पेड़ की सभी पत्तियों पर विचार करने के बाद, जो आप दुनिया के बारे में जानते हैं (जैसे जहाज ओवरलैप नहीं कर सकते हैं, सभी हिट वर्गों में जहाज होने चाहिए, आदि) आप यह गिन सकते हैं कि प्रत्येक अनिर्दिष्ट स्थिति में कितनी बार जहाजों की संभावना का अनुमान लगाया जा सकता है कि एक जहाज वहां बैठा है।

इसे ऊष्मा मानचित्र के रूप में देखा जा सकता है, जहाँ गर्म धब्बे जहाजों के होने की अधिक संभावना है:

प्रत्येक अस्पष्टीकृत स्थिति के लिए संभावनाओं का एक ऊष्मा मानचित्र http://natekohl.net/media/battleship-probs.png

इस बैटलशिप प्रतियोगिता के बारे में एक बात मुझे अच्छी लगती है कि ऊपर का पेड़ इस तरह के एल्गोरिदम को ब्रूट-फोर्स करने के लिए काफी छोटा है। यदि 5 जहाजों में से प्रत्येक के लिए ~ 150 संभावित स्थान हैं, तो 150 5 = 75 बिलियन संभावनाएं हैं। और यह संख्या केवल छोटी हो जाती है, खासकर यदि आप पूरे जहाजों को खत्म कर सकते हैं।

प्रतिद्वंद्वी जो मैंने ऊपर जोड़ा है वह पूरे पेड़ का पता नहीं लगाता है; 75 बिलियन एक सेकंड के अंदर प्राप्त करने के लिए अभी भी बड़ा है। यह इन संभावनाओं का अनुमान लगाने का प्रयास करता है, हालांकि, कुछ अनुमानों की मदद से।


अब तक, आप हमारे केवल दूसरे पूर्ण समाधान को लगभग 67.7% से 32.3% तक हरा रहे हैं :)
जॉन गीत्ज़ेन

2
मैं निश्चित रूप से यह देखने के लिए उत्सुक हूं कि "संभावना दृष्टिकोण" की तुलना "ज्यामितीय दृष्टिकोण" से कैसे की जाती है। मैंने देखा है कि यह संभावना प्रतिद्वंद्वी वास्तव में अन्य उत्तरों में चर्चा की गई ज्यामितीय पैटर्न का पालन करने वाली चालें बनाता है। यह हो सकता है कि ज्यामिति का उपयोग करना उतना ही अच्छा है, और बहुत तेज है। :)
नैट कोहल

12

पूरी तरह से उत्तर नहीं है, लेकिन कोड के साथ वास्तविक उत्तरों को कम करने वाली बात बहुत कम लगती है। मैं इस प्रकार खुले स्रोत की भावना में कुछ एक्सटेंशन / सामान्य कक्षाएं प्रस्तुत करता हूं। यदि आप इनका उपयोग करते हैं तो कृपया नाम स्थान को बदल दें या एक डीएलएल में सब कुछ संकलित करने की कोशिश न करें।

बोर्ड व्यू आपको एनोटेट बोर्ड के साथ आसानी से काम करने देता है।

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

कुछ विस्तार, इस में से कुछ मुख्य ढांचे में कार्यक्षमता की नकल करते हैं लेकिन वास्तव में आपके द्वारा किया जाना चाहिए।

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

कुछ मैं बहुत का उपयोग करके समाप्त होता हूं।

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

यादृच्छिकीकरण। सुरक्षित लेकिन परीक्षण योग्य, परीक्षण के लिए उपयोगी।

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

मेरे पास अभी पूर्ण-कालिक एल्गोरिथम लिखने का समय नहीं है, लेकिन यहां एक विचार है: यदि आपके प्रतिद्वंद्वी ने जहाजों को बेतरतीब ढंग से रखा है, तो क्या प्लेसमेंट संभावनाएं एक साधारण वितरण (5.5,5.5) पर केंद्रित नहीं होंगी? उदाहरण के लिए, एक्स आयाम में युद्धपोत (5 इकाइयों लंबे) के लिए प्लेसमेंट की संभावनाएं यहां हैं:

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

वही गणना y के लिए मान्य होगी। अन्य जहाजों में वितरण की स्थिति नहीं होगी, लेकिन आपका सबसे अच्छा अनुमान अभी भी केंद्र है। उसके बाद, गणितीय दृष्टिकोण धीरे-धीरे विकर्ण विकीर्ण होगा (शायद औसत जहाज की लंबाई के साथ, 17/5) केंद्र से बाहर। उदाहरण के लिए:

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

जाहिर है कि कुछ यादृच्छिकता को विचार में जोड़ा जाना चाहिए, लेकिन मुझे लगता है कि विशुद्ध रूप से गणितीय रूप से यही रास्ता तय करना है।


हाँ, वास्तव में वे करेंगे। मेरे पुराने इंजन ने उसके लिए मुआवजा दिया।
जॉन गीत्ज़ेन

1
जहां से मैं आता हूं, धीरे-धीरे विकर्ण को केंद्र से बाहर निकालने को धोखा माना जाता है
bzlm

यदि इसे धोखा माना जाता है, तो एक बहुत आसान प्रतिवाद है। से बचें (x, y) जहाँ x = y है। :)
ine

5
मुझे लगता है कि वह कार्ड काउंटिंग के लिए अलाउड कर रहा था? जो, मेरी राय में, धोखा नहीं है।
जॉन गिएट्ज़ेन

10

कुछ भी नहीं है कि परिष्कृत लेकिन मैं क्या साथ आया था। यह 99.9% समय के यादृच्छिक प्रतिद्वंद्वी को हराता है। दिलचस्पी होगी अगर किसी के पास इस तरह की कोई और छोटी-मोटी चुनौतियाँ हों तो अच्छा था।

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

यहाँ पर कम से कम जगह लेने के लिए थोड़ा घनीभूत और अभी भी पठनीय है।


6

प्रतियोगिता इंजन के बारे में कुछ टिप्पणियाँ:

NewGame पैरामीटर:

यदि IBattleshipOpponent :: NewGame प्री-गेम सेटअप के लिए अभिप्रेत है और एक बोर्डाइज लेता है, तो उसे जहाजों और उनके संबंधित आकारों की सूची भी लेनी चाहिए। यह चर जहाज विन्यास की अनुमति के बिना चर बोर्ड-आकार के लिए अनुमति देने के लिए कोई मतलब नहीं है।

जहाजों को सील कर दिया जाता है:

मुझे कोई कारण नहीं दिखता कि क्लास शिप को क्यों सील किया जाए। अन्य बुनियादी बातों के अलावा, मैं चाहूंगा कि जहाजों का एक नाम हो, इसलिए मैं संदेश भेज सकता हूं जैसे ("यू माय माय {0}", जहाज। नाम); । मेरे पास अन्य एक्सटेंशन भी हैं, इसलिए मुझे लगता है कि शिप को अंतर्निहित होना चाहिए।

समय सीमा:

जबकि 1 सेकंड की समय सीमा एक टूर्नामेंट नियम के लिए समझ में आती है, यह पूरी तरह से डिबगिंग के साथ खिलवाड़ करता है। विकास / डिबगिंग के साथ सहायता करने के लिए समय-उल्लंघन की अनदेखी करने के लिए युद्धपोत की स्थापना की आसान सेटिंग होनी चाहिए। मैं यह भी सुझाव दूंगा कि System.Diagnostics.Process :: UserProcessorTime / Privileged ProcessorTime / TotalProcessorTime के बारे में अधिक सटीक दृष्टिकोण के लिए कि कितना समय उपयोग किया जा रहा है।

सनक जहाजों:

वर्तमान एपीआई आपको सूचित करता है कि आपने एक दुर्घटनाग्रस्त जहाज को डूबो दिया है:

ShotHit(Point shot, bool sunk);

लेकिन जो आप डूब नहीं जहाज! मैं इसे मानव-युद्ध-प्रणाली के नियमों का हिस्सा मानता हूं, जिन्हें आपको "आप मेरी युद्ध-भूमि डूबने" के लिए घोषित करना चाहते हैं! (या विध्वंसक, या उप, आदि)।

यह विशेष रूप से महत्वपूर्ण है जब एक एआई जहाजों को एक दूसरे के खिलाफ बट-अप करने की कोशिश कर रहा है। मैं एपीआई परिवर्तन के लिए अनुरोध करना चाहता हूं:

ShotHit(Point shot, Ship ship);

यदि जहाज गैर-शून्य है, तो इसका मतलब है कि शॉट डूबने वाला शॉट था, और आप जानते हैं कि आप किस जहाज से डूब गए थे, और यह कितनी देर तक था। यदि शॉट एक गैर-डूबने वाला शॉट था, तो जहाज अशक्त है, और आपको और कोई जानकारी नहीं है।


कृपया कोड नमूने पोस्ट करें यदि आपको लगता है कि समय अधिक सटीक रूप से किया जा सकता है। मैं अभी बहुत ज्यादा नियम नहीं बदलना चाहता।
जॉन गीटजेन

इसके अलावा, प्लेसशिप () के दौरान जहाज के आकार को पारित किया जाता है जो कि खेल के अनुसार एक बार चलाया जाता है और इसे सेट-अप चरण के रूप में भी इस्तेमाल किया जा सकता है। कृपया अपने स्वयं के परीक्षण के लिए जहाज को मुक्त करने के लिए स्वतंत्र महसूस करें, लेकिन मैं टूर्नामेंट के लिए मुहरबंद एक का उपयोग करने की योजना बनाता हूं।
जॉन गीटजेन 20

बग: @ जॉन गीत्ज़ेन: मैंने निर्धारित किया है कि प्लेसिशिप खेल के अनुसार एक बार बिल्कुल नहीं चलाया जाता है (जैसा कि आपने बताया)। यदि कोई खिलाड़ी अपने जहाजों को गलत तरीके से रखता है (जैसा कि रैंडम ऑपॉपर अक्सर करता है), तो प्लेसस्शिप को बार-बार बुलाया जाता है, बिना किसी न्यूगैम कॉल के।
अबेलेंकी

5
मैंने हमेशा अपने प्रतिद्वंद्वी को यह सोचने के लिए एल कॉन्फ़िगरेशन में दो जहाजों को रखने की रणनीति पर विचार किया कि वे एक युद्धपोत डूब गए थे जब वास्तव में वे नहीं थे। मैं कभी इस धारणा के अधीन नहीं था कि आपको यह घोषित करना होगा कि कौन सी नाव डूब गई।
जोश स्मेटन

3
@DJ: मैं मूल पेन-एंड-पेपर नियमों द्वारा जा रहा हूं। याद रखें कि हैस्ब्रो एक खिलौना कंपनी है और यह खेल हस्ब्रो से पहले का है।
जॉन गीत्ज़ेन

5

क्रॉसफ़ायर अपडेट किया गया। मुझे पता है कि यह Farnsworth या Dreadnought के साथ प्रतिस्पर्धा नहीं कर सकता है, लेकिन यह बाद की तुलना में बहुत तेज है और अगर कोई भी कोशिश करना चाहता है तो इसे खेलना आसान है। यह मेरे पुस्तकालयों की वर्तमान स्थिति पर निर्भर करता है, यहां इसका उपयोग करना आसान है।

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

यह सबसे अच्छे के बारे में है जो मैं अपने खाली समय में एक साथ रख सकता था, जो गैर-मौजूद है। कुछ गेम और मैच टैलींग आँकड़े चल रहे हैं, जैसा कि मैंने मुख्य कार्य को लूप में सेट किया और एक कुंजी दबाए जाने तक लगातार बैटलशिपकंपीशन चला रहा था।

namespace Battleship
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Linq;

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

        Random rand = new Random();
        Version version = new Version(0, 7);
        Size gameSize;
        List<Point> scanShots;
        List<NextShot> nextShots;
        int wins, losses;
        int totalWins = 0;
        int totalLosses = 0;
        int maxWins = 0;
        int maxLosses = 0;
        int matchWins = 0;
        int matchLosses = 0;

        public enum Direction { VERTICAL = -1, UNKNOWN = 0, HORIZONTAL = 1 };
        Direction hitDirection, lastShotDirection;

        enum ShotResult { UNKNOWN, MISS, HIT };
        ShotResult[,] board;

        public struct NextShot
        {
            public Point point;
            public Direction direction;
            public NextShot(Point p, Direction d)
            {
                point = p;
                direction = d;
            }
        }

        public struct ScanShot
        {
            public Point point;
            public int openSpaces;
            public ScanShot(Point p, int o)
            {
                point = p;
                openSpaces = o;
            }
        }

        public void NewGame(Size size, TimeSpan timeSpan)
        {
            this.gameSize = size;
            scanShots = new List<Point>();
            nextShots = new List<NextShot>();
            fillScanShots();
            hitDirection = Direction.UNKNOWN;
            board = new ShotResult[size.Width, size.Height];
        }

        private void fillScanShots()
        {
            int x;
            for (x = 0; x < gameSize.Width - 1; x++)
            {
                scanShots.Add(new Point(x, x));
            }

            if (gameSize.Width == 10)
            {
                for (x = 0; x < 3; x++)
                {
                    scanShots.Add(new Point(9 - x, x));
                    scanShots.Add(new Point(x, 9 - x));
                }
            }
        }

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

        public Point GetShot()
        {
            Point shot;

            if (this.nextShots.Count > 0)
            {
                if (hitDirection != Direction.UNKNOWN)
                {
                    if (hitDirection == Direction.HORIZONTAL)
                    {
                        this.nextShots = this.nextShots.OrderByDescending(x => x.direction).ToList();
                    }
                    else
                    {
                        this.nextShots = this.nextShots.OrderBy(x => x.direction).ToList();
                    }
                }

                shot = this.nextShots.First().point;
                lastShotDirection = this.nextShots.First().direction;
                this.nextShots.RemoveAt(0);
                return shot;
            }

            List<ScanShot> scanShots = new List<ScanShot>();
            for (int x = 0; x < gameSize.Width; x++)
            {
                for (int y = 0; y < gameSize.Height; y++)
                {
                    if (board[x, y] == ShotResult.UNKNOWN)
                    {
                        scanShots.Add(new ScanShot(new Point(x, y), OpenSpaces(x, y)));
                    }
                }
            }
            scanShots = scanShots.OrderByDescending(x => x.openSpaces).ToList();
            int maxOpenSpaces = scanShots.FirstOrDefault().openSpaces;

            List<ScanShot> scanShots2 = new List<ScanShot>();
            scanShots2 = scanShots.Where(x => x.openSpaces == maxOpenSpaces).ToList();
            shot = scanShots2[rand.Next(scanShots2.Count())].point;

            return shot;
        }

        int OpenSpaces(int x, int y)
        {
            int ctr = 0;
            Point p;

            // spaces to the left
            p = new Point(x - 1, y);
            while (p.X >= 0 && board[p.X, p.Y] == ShotResult.UNKNOWN)
            {
                ctr++;
                p.X--;
            }

            // spaces to the right
            p = new Point(x + 1, y);
            while (p.X < gameSize.Width && board[p.X, p.Y] == ShotResult.UNKNOWN)
            {
                ctr++;
                p.X++;
            }

            // spaces to the top
            p = new Point(x, y - 1);
            while (p.Y >= 0 && board[p.X, p.Y] == ShotResult.UNKNOWN)
            {
                ctr++;
                p.Y--;
            }

            // spaces to the bottom
            p = new Point(x, y + 1);
            while (p.Y < gameSize.Height && board[p.X, p.Y] == ShotResult.UNKNOWN)
            {
                ctr++;
                p.Y++;
            }

            return ctr;
        }

        public void NewMatch(string opponenet)
        {
            wins = 0;
            losses = 0;
        }

        public void OpponentShot(Point shot) { }

        public void ShotHit(Point shot, bool sunk)
        {
            board[shot.X, shot.Y] = ShotResult.HIT;

            if (!sunk)
            {
                hitDirection = lastShotDirection;
                if (shot.X != 0)
                {
                    this.nextShots.Add(new NextShot(new Point(shot.X - 1, shot.Y), Direction.HORIZONTAL));
                }

                if (shot.Y != 0)
                {
                    this.nextShots.Add(new NextShot(new Point(shot.X, shot.Y - 1), Direction.VERTICAL));
                }

                if (shot.X != this.gameSize.Width - 1)
                {
                    this.nextShots.Add(new NextShot(new Point(shot.X + 1, shot.Y), Direction.HORIZONTAL));
                }

                if (shot.Y != this.gameSize.Height - 1)
                {
                    this.nextShots.Add(new NextShot(new Point(shot.X, shot.Y + 1), Direction.VERTICAL));
                }
            }
            else
            {
                hitDirection = Direction.UNKNOWN;
                this.nextShots.Clear();     // so now this works like gangbusters ?!?!?!?!?!?!?!?!?
            }
        }

        public void ShotMiss(Point shot)
        {
            board[shot.X, shot.Y] = ShotResult.MISS;
        }

        public void GameWon()
        {
            wins++;
        }

        public void GameLost()
        {
            losses++;
        }

        public void MatchOver()
        {
            if (wins > maxWins)
            {
                maxWins = wins;
            }

            if (losses > maxLosses)
            {
                maxLosses = losses;
            }

            totalWins += wins;
            totalLosses += losses;

            if (wins >= 51)
            {
                matchWins++;
            }
            else
            {
                matchLosses++;
            }
        }

        public void FinalStats()
        {
            Console.WriteLine("Games won: " + totalWins.ToString());
            Console.WriteLine("Games lost: " + totalLosses.ToString());
            Console.WriteLine("Game winning percentage: " + (totalWins * 1.0 / (totalWins + totalLosses)).ToString("P"));
            Console.WriteLine("Game losing percentage: " + (totalLosses * 1.0 / (totalWins + totalLosses)).ToString("P"));
            Console.WriteLine();
            Console.WriteLine("Matches won: " + matchWins.ToString());
            Console.WriteLine("Matches lost: " + matchLosses.ToString());
            Console.WriteLine("Match winning percentage: " + (matchWins * 1.0 / (matchWins + matchLosses)).ToString("P"));
            Console.WriteLine("Match losing percentage: " + (matchLosses * 1.0 / (matchWins + matchLosses)).ToString("P"));
            Console.WriteLine("Match games won high: " + maxWins.ToString());
            Console.WriteLine("Match games lost high: " + maxLosses.ToString());
            Console.WriteLine();
        }
    }
}

यह तर्क निकटतम है कि मुझे लगभग 41% व्यक्तिगत खेलों में जीतते हुए, ड्रेडनॉट की पिटाई करनी पड़ी। (यह वास्तव में 52 से 49 की गिनती से एक मैच जीता था।) विचित्र रूप से पर्याप्त है, यह वर्ग पहले के संस्करण के रूप में FarnsworthOpponent के खिलाफ भी ऐसा नहीं करता है जो बहुत कम उन्नत था।


5

अभी मेरे कंप्यूटर की मरम्मत dell द्वारा की जा रही है, लेकिन यह वह जगह है जहाँ मैं पिछले सप्ताह था:

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
रजत को बधाई। क्या आप अपने एल्गोरिथ्म का वर्णन शब्दों में करते हैं? इसके बारे में जानना दिलचस्प होगा।
थॉमस अहले

4

यदि आप अपने विश्लेषण के लिए मजबूर कर रहे हैं, तो आप अत्यधिक बेतरतीब ढंग से आपूर्ति किए गए रैंडम ओपेन मैकेनिक को देख सकते हैं। यह खुद को पहले से ही लक्षित स्थानों को फिर से आकार देने की अनुमति देता है और फ्रेमवर्क को दोहराने के लिए मजबूर करता है जब तक कि यह हिट नहीं हो जाता है जब तक कि यह अभी तक नहीं छूता है या प्रति कदम समय सीमा समाप्त हो जाती है।

इस प्रतिद्वंद्वी का व्यवहार समान है (प्रभावी प्लेसमेंट वितरण समान है) यह सिर्फ पवित्रता की जाँच करता है और प्रति कॉल केवल एक यादृच्छिक संख्या का उपभोग करता है (amortized)।

यह मेरे एक्सटेंशन / लाइब्रेरी उत्तर में कक्षाओं का उपयोग करता है और मैं केवल प्रमुख विधियों / स्थिति की आपूर्ति करता हूं।

यहां पर जॉन स्कीट के जवाब से शफल को हटा दिया गया है

class WellBehavedRandomOpponent : IBattleShipOpponent
{
    Rand rand = new Rand();
    List<Point> guesses;
    int nextGuess = 0;

    public void PlaceShips(IEnumerable<Ship> ships)
    {
        BoardView<bool> board = new BoardView<bool>(BoardSize);
        var AllOrientations = new[] {
            ShipOrientation.Horizontal,
            ShipOrientation.Vertical };

        foreach (var ship in ships)
        {
            while (!ship.IsPlaced)
            {
                var l = rand.Pick(board.Select(c => c.Location));
                var o = rand.Pick(AllOrientations);
                if (ship.IsLegal(ships, BoardSize, l, o))
                    ship.Place(l, o);
            }
        }
    }

    public void NewGame(Size size, TimeSpan timeSpan)
    {
        var board = new BoardView<bool>(size);
        this.guesses = new List<Point>(
            board.Select(x => x.Location).Shuffle(rand));
        nextGuess = 0;
    }

    public System.Drawing.Point GetShot()
    {
        return guesses[nextGuess++];
    }

    // empty methods left out 
}

4

मैं भाग लेने में सक्षम नहीं होने जा रहा हूँ, लेकिन यहाँ एल्गोरिथ्म मैं लागू होता अगर मेरे पास समय होता:

सबसे पहले, जब मैं एक हिट का पता लगाता हूं तो मैं तुरंत जहाज के बाकी हिस्सों का पीछा नहीं करता हूं - मैं जहाज के स्थानों की एक तालिका बनाता हूं और यह पता लगाता हूं कि क्या मैंने उन्हें पूरी तरह से डूबने से पहले शुरू करने से पहले कम से कम एक बार सभी पांचों को मारा है। (ध्यान दें कि यह कई शॉट संस्करण के लिए एक खराब नीति है - टिप्पणियां देखें)

  1. केंद्र को मारो (नीचे अंतिम नोट देखें - 'केंद्र' वर्णन के लिए सिर्फ एक सुविधा है)
  2. केंद्र के दाईं ओर स्पॉट 4 मारो
  3. स्पॉट 1 नीचे मारो और केंद्र के दाईं ओर एक
  4. पिछली हिट के दाईं ओर स्पॉट को हिट करें
  5. उस पैटर्न में जारी रखें (बोर्ड को भरने वाले 3 स्थानों द्वारा अलग किए गए विकर्ण लाइनों के साथ समाप्त होना चाहिए) यह सभी 4 और 5 लंबाई वाली नावों, और 3 और 2 नावों की सांख्यिकीय बड़ी संख्या में हिट होना चाहिए।

  6. विकर्णों को बेतरतीब ढंग से मारना शुरू करें, यह 2 और 3 लंबाई वाली नावों को पकड़ लेगा जो पहले से नजर नहीं आई हैं।

एक बार जब मैंने 5 हिट का पता लगाया है, तो मैं निर्धारित करूंगा कि क्या 5 हिट अलग-अलग नावों पर हैं। यह उन स्थानों के पास कुछ अधिक शॉट्स बनाकर अपेक्षाकृत आसान है जहां दो हिट एक ही क्षैतिज या ऊर्ध्वाधर रेखा पर हैं और एक दूसरे के 5 स्थानों के भीतर हैं (एक ही नाव पर दो हिट हो सकते हैं)। यदि वे अलग-अलग नावें हैं तो सभी जहाजों को डुबोना जारी रखें। यदि वे समान नाव पाए जाते हैं, तो ऊपर दिए गए भरने के पैटर्न को तब तक जारी रखें जब तक कि सभी 5 नावें स्थित न हों।

यह एल्गोरिथ्म एक साधारण फिलिंग एल्गोरिथम है। प्रमुख विशेषताएं यह है कि यह डूबने वाले जहाजों को समय बर्बाद नहीं करता है, इसके बारे में जानता है कि अभी भी ऐसे जहाज हैं जिनसे यह अनजान है, और यह एक अक्षम भरने वाले पैटर्न का उपयोग नहीं करता है (यानी, एक पूरी तरह से यादृच्छिक पैटर्न बेकार होगा)।

अंतिम नोट:

ए) "केंद्र" बोर्ड पर एक यादृच्छिक प्रारंभिक बिंदु है। यह इस एल्गोरिथ्म की प्राथमिक कमजोरी को समाप्त करता है। बी) जबकि वर्णन शुरू से तुरंत विकर्णों को इंगित करता है, आदर्श रूप से एल्गोरिथ्म केवल 'यादृच्छिक' स्थानों पर शूट करता है जो उन विकर्णों के साथ होते हैं। इससे प्रतियोगी को समय से रोकने में मदद मिलती है जब तक कि उनके जहाजों को पूर्वानुमानित पैटर्न द्वारा हिट नहीं किया जाता है।

यह एक 'सही' एल्गोरिथ्म का वर्णन करता है कि इसमें सभी जहाजों को (9x9) / 2 + 10 शॉट्स मिलेंगे।

हालाँकि, इसमें काफी सुधार किया जा सकता है:

एक बार जब कोई जहाज मारा जाता है, तो 'आंतरिक' विकर्ण लाइनों को करने से पहले उसके आकार की पहचान करें। आपको 2 जहाज मिल सकते हैं, ऐसे में आंतरिक आकार के 3 आकारों के जहाजों को और अधिक तेज़ी से खोजने के लिए सरल बनाया जा सकता है।

खेल में चरणों को पहचानें और उसके अनुसार कार्य करें। यह एल्गोरिदम खेल में एक निश्चित बिंदु तक अच्छा हो सकता है, लेकिन अन्य एल्गोरिदम एंडगेम के हिस्से के रूप में बेहतर लाभ दे सकते हैं। इसके अलावा, यदि दूसरा खिलाड़ी आपको हराने के बहुत करीब है, तो दूसरा एल्गोरिथ्म बेहतर काम कर सकता है - उदाहरण के लिए एक उच्च जोखिम एल्गोरिथ्म अधिक बार विफल हो सकता है, लेकिन जब यह काम करता है तो यह जल्दी से काम करता है और आप अपने प्रतिद्वंद्वी को हरा सकते हैं जो आपसे जीतने के करीब है। ।

प्रतियोगी की खेलने की शैली को पहचानें - यह आपको सुराग दे सकता है कि वे शिप प्लेसमेंट की योजना कैसे बनाते हैं (यानी, संभावनाएं अच्छी हैं कि उनका अपना एल्गोरिथ्म सबसे तेज़ी से पहचानता है कि वे अपने जहाजों को कैसे रखते हैं - यदि आपके पास एकमात्र उपकरण हथौड़ा है, सब कुछ नाखून की तरह दिखता है)

-Adam


सभी मिलने तक जहाजों को डूबने की प्रतीक्षा की रणनीति एक-शॉट-प्रति-मोड़ भिन्नता पर निर्भर करती है। (जीवित जहाजों की संख्या) के तहत -shots- प्रति-मोड़ संस्करण, अपने प्रतिद्वंद्वी को धीमा करने के लिए जितनी जल्दी हो सके जहाजों को सिंक करना फायदेमंद है।
जेसन ओवेन

4

मेरी एंट्री।

कुछ खास नहीं, और मुझे उन सभी अच्छे विचारों को जोड़ने का समय नहीं मिला जो मेरे पास थे।

लेकिन यह काफी अच्छा खेल रहा है। हम देखेंगे कि यह प्रतियोगिता में कैसा है:

(इसे फाइल में डालें Missouri.csऔर प्रोजेक्ट में जोड़ा जाए।)

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

और अब जब मैंने अपनी प्रविष्टि प्रस्तुत की है, तो कुछ मोटे आँकड़े: बनाम बीपी 7 44% जीतता है। / बनाम Dreadnought 20% जीत। / बनाम फ़ार्न्सवर्थ 42% जीतता है। यह एक मजेदार प्रोजेक्ट था।
abelenky

2

यह न्यूनतम नहीं है। वास्तव में जहाजों को रखने के बाद, प्रत्येक खिलाड़ी अपने दम पर नहीं खेल सकता है, जिसके परिणामस्वरूप प्रत्येक प्रतिद्वंद्वी जहाज को डूबने के लिए कई मोड़ आए? जिसने कम जीत हासिल की, वह जीत गया।

मुझे नहीं लगता कि डूबते जहाजों से परे कोई अच्छी सामान्य रणनीति है और शेष संभावित स्थानों को कवर करने के लिए शॉट्स की संख्या को कम करने की कोशिश कर रहे हैं जहां जहाज छिपते हैं।

बेशक ऐसी किसी भी चीज़ के लिए प्रति-रणनीति हो सकती है जो यादृच्छिक नहीं है। लेकिन मुझे नहीं लगता कि ऐसी कोई रणनीति है जो सभी संभावित खिलाड़ियों के खिलाफ अच्छी हो।


1
संभावित रूप से, हाँ, वे अपने दम पर खेल सकते थे। यह नहीं है कि यह कैसे चलाया जाएगा। महान विचार, यद्यपि। इस प्रतियोगिता में, मैं चाहता हूं कि अपने प्रतिद्वंद्वी के शॉट्स से सांख्यिकीय रूप से बचना संभव हो।
जॉन गीत्जन

2
समझा। एक ही प्रतिद्वंद्वी के खिलाफ पूर्व खेल से डेटा का उपयोग करने के लिए उसे अनुकूलित करने में सक्षम हो सकता है?
जिग्गीस्टार

2

वास्तव में, मुझे लगता है कि पहेली के साथ सबसे बड़ी समस्या यह है कि इसकी अनिवार्य रूप से दो चालें हैं। एक चाल आपके जहाजों को चला रही है, दूसरा दुश्मन जहाजों को ढूंढ रहा है (हालांकि खंडित है कि दूसरा हिस्सा हो सकता है, एक यादृच्छिक कारक के साथ एक घड़ी को हराने की कोशिश करने से अलग, इसके बस 'अपने एल्गोरिथ्म को चलाएं')। निर्धारित करने की कोशिश करने और फिर दुश्मन की रणनीति का मुकाबला करने के लिए कोई तंत्र नहीं है, जो कि "रॉक पेपर कैंची" के क्रमिक दौरों के आधार पर इसी तरह की प्रतियोगिताओं को बहुत दिलचस्प बनाता है।

इसके अलावा, मुझे लगता है कि यदि आप गेम को नेटवर्क प्रोटोकॉल के रूप में निर्दिष्ट करते हैं तो यह कूलर होगा और फिर उस प्रोटोकॉल को C # में लागू करने के लिए ढांचा प्रदान किया गया, बजाय इसके कि सभी समाधान C # होने चाहिए, लेकिन यह सिर्फ मेरी राय है।

संपादित करें: जब से मैंने प्रतियोगिता नियमों को ध्यान से नहीं पढ़ा, मैं अपने प्रारंभिक बिंदु को रद्द कर देता हूं।


सभी समाधान C # में नहीं होने चाहिए। मैं एक अलग विधानसभा में संकलन और लिंक कर सकता हूं। इसके अलावा, आपको अपने प्रतिद्वंद्वी का सांख्यिकीय रूप से मुकाबला करने में सक्षम होना चाहिए।
जॉन गीत्जन

जे#? शायद? हाहा सिर्फ मजाक कर रहा हूं। मेरे पास इसके लिए एक टीसीपी ढांचा है, लेकिन इस टूर्नामेंट को बहुत जल्दी चलाने की जरूरत है।
जॉन गीत्ज़ेन

आप यह क्यों मानेंगे कि एक ही मशीन पर दो प्रक्रियाओं के बीच टीसीपी संचार तेजी से नहीं होगा?
झेरिको

@ जेरिको: अगर मैं टीसीपी का उपयोग कर रहा था, तो मैं अपने पीसी पर इंजनों को अलग करूंगा ताकि वे किसी भी सीपीयू संसाधनों का उपयोग कर सकें जो वे चाहते थे।
जॉन गीटजेन

फिर भी, एक ही लेन पर दो मशीनें आसानी से एक गेम के तहत एक गेम को पूरा कर सकती हैं, जिसमें नेटवर्क ओवरहेड न्यूनतम है
झेरिको

2

मैं हमेशा बीच में शुरू करना पसंद करता था और उस गॉडडैम उप के लिए खाते में किसी भी अन्य बिंदुओं के बीच 1 से अधिक रिक्त स्थान नहीं छोड़ने से एक बिंदु से दूर सर्पिलिंग करना ... शॉट्स के बीच का स्थान इस बात पर निर्भर करता था कि जहाज डूब गए थे। यदि बी-जहाज अंतिम था, तो शॉट्स को केवल व्यर्थ शॉट को कम करने के लिए बीच में 4 स्थान छोड़ने पड़ते थे


1
तो ... मैं बस बीच से दूर रहने की जरूरत है? :)
डे्रॉन

14
आपको किनारों से दूर रहने की भी आवश्यकता है, क्योंकि एक बढ़त हिट में आपके प्रतिद्वंद्वी के लिए गैर-किनारे हिट की तुलना में अधिक जानकारी होती है। इसलिए आपको अपने सभी जहाजों को एक गैर-मध्य, गैर-किनारे वाले क्षेत्र में रखना चाहिए। जब तक कि वे आपसे क्या करने की उम्मीद कर रहे हैं।
झिरिको

1
यदि आप 3 या 4 स्थानों को छोड़कर शुरू करते हैं, तो आप उप भाग्यशाली हो सकते हैं वैसे भी उप हिट करने के लिए। यदि नहीं, तो वापस जाएं और अंतराल में भरने का प्रयास करें। और अधिक: somethinkodd.com/oddthinking/2009/10/29/battleship-strategy
ऑडिटिंग

18
दो छेद वाला जहाज एक गोड्डम उप नहीं है , यह एक गोड्डम पीटी बोट है । उप में तीन छेद होते हैं। :)
रेवेन

2

ब्रिटिश कंप्यूटर सोसाइटी की ओर से द यूनिवर्सिटी ऑफ सरे के डॉ। जेम्स हीथर द्वारा इसी तरह की एक प्रतियोगिता आयोजित की गई थी।

सीमाओं को संसाधनों पर रखा गया था - अर्थात् अधिकतम प्रोसेसर समय प्रति मोड़, कोई राज्य चाल के बीच संग्रहीत नहीं किया जा सकता, अधिकतम ढेर आकार लगाया गया। समय को सीमित करने के लिए, एआई समय स्लॉट के भीतर किसी भी बिंदु पर एक कदम जमा कर सकता है और मोड़ को समाप्त करने पर एक कदम के लिए कहा जाएगा।

बहुत दिलचस्प - पर अधिक देखें: http://www.bcsstudentcontest.com/

आपको कुछ और विचार दे सकते हैं।


2

जैसा कि यह है, समाधान खुलता है और ubuntu 9.10 linux में मोनोडेवलप में बिना किसी संशोधन के चलता है


1

आप ने लिखा:

  • प्रतियोगिता की भावना के खिलाफ जो भी समझा जाता है, वह अयोग्यता के लिए आधार होगा।
  • एक प्रतिद्वंद्वी के साथ हस्तक्षेप करना प्रतियोगिता की भावना के खिलाफ है।

कृपया "प्रतियोगिता की भावना के खिलाफ" और "एक प्रतिद्वंद्वी के साथ हस्तक्षेप" को परिभाषित करें?

इसके अलावा - सरल बनाने के लिए, मैं सुझाव देता हूं कि आप:

  • प्रतिद्वंद्वी के सीपीयू स्लॉट के दौरान सीपीयू का उपयोग करना बंद करें।
  • थ्रेड समांतरता को समाप्त करें और इसके बजाय एक ही थ्रेड पर अधिक CPU सेकंड दें। यह AI की प्रोग्रामिंग को सरल करेगा और जो भी CPU / मेमोरी-बाउंड है, किसी को भी चोट नहीं पहुंचाएगा।

पुनश्च - CS के लिए एक प्रश्न यहाँ दुबका हुआ है: क्या यह गेम सॉल्व नहीं है (यानी एक सिंगल, बेस्ट स्ट्रेटेजी है?)। हां, बोर्ड का आकार और चरणों की संख्या न्यूनतम एट अल को अनिवार्य बनाती है, लेकिन फिर भी मुझे आश्चर्य करना होगा ... यह गो से दूर है और जटिलता में शतरंज है।


जब मैंने "इंटरफेरिंग" कहा तो मेरे मन में प्रतिबिंब था। मैं प्रतियोगियों को जीतना नहीं चाहता क्योंकि उन्होंने मौत के लिए दूसरे इंजन को थोड़ा-सा मोड़ दिया।
जॉन गीटजेन

8
मेरा सुझाव है कि जासूसी आधुनिक युद्ध का एक महत्वपूर्ण हिस्सा है, इसलिए लक्ष्यों को खोजने के लिए प्रतिबिंबित करना आदर्श होगा - आखिरकार, यह दूसरे विश्व युद्ध के दौरान इस्तेमाल किए जाने वाले तरीकों में से एक था ...
रोलैंड शॉ

मेरे पास अलग-अलग पीसी पर इंजन को अलग करने के लिए एक फ्रेमवर्क है, जो टीसीपी / आईपी पर संचार करता है, रिफ्लेक्शन को बेकार करता है। हालांकि, मेरी अनुमानित संख्या की प्रविष्टियों के कारण, यह प्रतियोगिता को काफी लंबे समय तक ले जाएगा।
जॉन गीत्ज़ेन

6
मुझे नहीं पता था कि उनके पास वापस प्रतिबिंब था!
मार्कस निगबोर

1

मुझे लगता है कि जो व्यक्ति अपने विरोधियों को यादृच्छिक बीज और कॉल पैटर्न को उलटने के लिए प्रबंधन करता है वह जीत जाएगा।

यह निश्चित नहीं है कि इसकी संभावना कितनी है।


विरोधियों के पास CSPRNG का उपयोग करने का विकल्प है।
जॉन गिएट्ज़ेन

अच्छी बात है, हालाँकि मैं मानता हूँ कि इस तरह के बीज को उल्टा करना वैसे भी मेरी विशेषज्ञता से परे है। मुझे लगता है कि सबसे कमजोर पहलू फायर पैटर्न चयन एल्गोरिदम होगा- लेकिन फिर भी आप जरूरी नहीं कि इसे तोड़ने से बहुत कुछ हासिल करेंगे, क्योंकि खेल शुरू होने के बाद आप अपने जहाजों को स्थानांतरित नहीं कर सकते।
ट्रिस्टन एट्रीज

जब मैं रिसर्च इंटर्नशिप के लिए आवेदन कर रहा था, तो हमने युद्धपोत कार्यक्रम लिखे और प्रतिस्पर्धा की। रैंडम सीड सेट करने से ठीक उसी तरह से जैसे मैंने एक्स जीता)
पी शेव्ड

1
एक यथोचित-सरल जहाज प्लेसमेंट एल्गोरिदम को मानते हुए, मैं कल्पना करूँगा कि विभिन्न जहाजों पर कुछ हिट होने के बाद, सभी संभव यादृच्छिक बीजों के माध्यम से एक के सबसे अधिक मोड़ का उपयोग करना शुरू करें (शायद वर्तमान समय के पास कहीं से शुरू होकर आगे की ओर बढ़ें / पीछे की ओर एक कदम या तो) और देखते हैं कि कौन से लोग जहाज के निर्माण को मनाया हिट के साथ संगत करते हैं।
दोमनी

1

यह भी, संभवतः, खेल पर बदलाव के साथ इनमें से एक श्रृंखला चलाना संभव होगा।

3 डी प्लेन जैसी चीजों में जोड़ना या एक बारी के लिए शूट के बजाय एक एकल जहाज को स्थानांतरित करने में सक्षम होना शायद खेल को काफी हद तक बदल देगा।


2
"साल्वो" भिन्नता है। जहां आपको प्रति मिनट कई शॉट्स शूट करने को मिलते हैं, जब आपके पास जहाज शेष होते हैं।
जॉन गीत्जन

एक दिलचस्प बदलाव भी। मुझे एक कंप्यूटर संस्करण खेलना याद आ रहा है जिसमें एक विमान भी था। यह बेतरतीब ढंग से विरोधी बोर्ड के स्थानों पर आग लगाएगा।
ग्लेन

एक और भिन्नता: जहाजों के बोर्ड + संख्या का आकार हो।
रसाऊ

1

एक दूसरे कुल खेल का समय मशीन विशिष्ट है। टूर्नामेंट मशीन की तुलना में एक बार सीपीयू संचालन का दूसरा मूल्य मेरी मशीन पर अलग होगा। यदि मैं 1 सेकंड के भीतर सबसे सीपीयू समय का उपयोग करने के लिए बैटल शिप एल्गोरिदम का अनुकूलन करता हूं, तो यह एक संभावित धीमी टूर्नामेंट मशीन पर चलाया जाता है, यह हमेशा खो जाएगा।

मुझे यकीन नहीं है कि ढांचे की इस सीमा के आसपास कैसे पहुंचें, लेकिन इसे संबोधित किया जाना चाहिए।

...

एक विचार यह करना है कि इस प्रतियोगिता में क्या किया गया था http://www.bcsstudentcontest.com /

और अधिकतम खेल समय के विपरीत प्रति बार अधिकतम मोड़ है। इस तरह मैं एल्गोरिदम को एक टर्न टाइम के भीतर फिट करने के लिए सीमित कर सकता था। यदि मेरा एल्गोरिथ्म अपने कुल खेल समय का प्रबंधन करता है, तो यह गेम 50 से 600+ तक चल सकता है, यह अपना सर्वश्रेष्ठ काम करने के लिए पर्याप्त समय नहीं दे सकता है या यह बहुत अधिक समय और खो सकता है। बैटलशिप एल्गोरिथ्म के भीतर कुल गेम समय का प्रबंधन करना बहुत कठिन है।

मैं सुझाव दूंगा कि कुल खेल समय नहीं, टर्न टाइम को सीमित करने के लिए नियम बदलने चाहिए।

संपादित करें

अगर मैंने एक एल्गोरिथ्म लिखा है जो सभी संभावित शॉट्स की गणना करता है और फिर उन्हें रैंक करता है, तो उच्चतम रैंकिंग शॉट लेता है। सभी संभावित शॉट्स को उत्पन्न करने में बहुत लंबा समय लगेगा, इसलिए मैं एल्गोरिथ्म को एक निश्चित समय तक चलने दूंगा, फिर इसे रोक दूंगा।

यदि टर्न-आधारित सीमा थी, तो मैं एल्गोरिथ्म को 0.9 सेकंड के लिए चलने दे सकता था और उच्चतम रैंकिंग शॉट लौटा सकता था, और बारी-बारी की सीमा को ठीक कर सकता था।

यदि मैं एक सेकंड के कुल खेल समय तक सीमित हूं, तो यह निर्धारित करना मुश्किल होगा कि एल्गोरिथ्म को प्रत्येक मोड़ के लिए कितनी देर तक चलना चाहिए। मैं अपना सीपीयू समय अधिकतम करना चाहूंगा। अगर कोई गेम 500 राउंड तक चलता है तो मैं प्रत्येक मोड़ को 0.002 सेकंड तक सीमित कर सकता हूं, लेकिन अगर कोई गेम 100 राउंड तक चलता है तो मैं सीपीयू समय के प्रत्येक सेकंड को 0.01 सेकंड का समय दे सकता हूं।

एक एल्गोरिथ्म के लिए यह अव्यावहारिक होगा कि शॉट की जगह की अर्ध-थकाऊ खोज का उपयोग करके वर्तमान सीमा के साथ सबसे अच्छा शॉट ढूंढा जाए।

1 सेकंड का कुल खेल समय एल्गोरिदम के प्रकार को सीमित कर रहा है जो कि खेल में प्रतिस्पर्धा करने के लिए प्रभावी रूप से उपयोग किया जा सकता है।


यह एक इंटेल Q9550SX क्वाड कोर, 8 जीबी रैम, विस्टा 64 मशीन पर चलाया जाएगा। क्या 1 सेकंड एक सीमित कारक है?
जॉन गित्जन

मुझे लगता है कि आपको अपने युद्धपोत एआई को बहुस्तरीय बनाना चाहिए, फिर, उस समय के अंतराल पर अधिकतम # शॉट्स की गणना करने के लिए।
जेफ एटवुड

चाल कैसे समय अंतराल को सीमित करने के लिए है। अगर मैं इसे 0.00005 सेकंड सीमित करता हूं तो मैं समय सीमा से अधिक सुरक्षित हूं, लेकिन मैं खोज स्थान को सीमित कर रहा हूं। यदि मैं मोड़ समय सीमा बढ़ाता हूं, तो खोज स्थान बढ़ जाता है लेकिन मैं समय से बाहर चलने का जोखिम उठाता हूं।
टोनीअबेल

@TonyAbell: यदि टर्न-आधारित समय सीमा का होना महत्वपूर्ण है, तो एक प्रारंभिक मूल्य के साथ शुरू क्यों न करें और फिर इसे गोल से गोल में समायोजित करें? लगभग आधे राउंड के बाद आपको सबसे अधिक संभावना होगी कि आप जिस प्रतिद्वंद्वी का सामना कर रहे हैं उसके लिए इष्टतम मोड़ की लंबाई मिल जाएगी।
kyokley

आपको अपना समय बचा हुआ रखना चाहिए, और इसे उपलब्ध समय के 1/2 तक सीमित रखना चाहिए।
जॉन गीत्ज़ेन

1

मैं वास्तविक कोड नहीं डालकर यहां नकल कर रहा हूं - लेकिन मैं कुछ सामान्य टिप्पणियों को खतरे में डालूंगा:

  • चूंकि सभी जहाज आकार में कम से कम 2 कोशिकाएं हैं, आप अंतरिक्ष क्वेस्ट V में खेल के कार्यान्वयन पर देखे गए एक अनुकूलन का उपयोग कर सकते हैं - जो केवल एक हीरे के पैटर्न में वैकल्पिक कोशिकाओं पर आग लगाता है, जबकि यह एक लक्ष्य की "तलाश" कर रहा है। यह आधे वर्गों को समाप्त कर देता है, जबकि अभी भी गारंटी है कि आप सभी जहाजों को अंततः पाएंगे।
  • लक्ष्य प्राप्त करने के दौरान एक यादृच्छिक फायरिंग पैटर्न सांख्यिकीय रूप से कई खेलों में सर्वोत्तम परिणाम देगा।

1

[संभावना घनत्व] [१] छवि विवरण दर्ज करें

[[यहाँ छवि विवरण दर्ज करें] [२]

मैंने रंडन शूटिंग बनाम डंब हंट / टार्गेट और अंत में एक परिष्कृत खोज के परिणामों की तुलना करने के साथ प्रयोग किया।

शेष जहाजों द्वारा किसी भी व्यक्तिगत वर्ग का उपयोग करने की संभावना के लिए सबसे अच्छा समाधान एक संभावना घनत्व समारोह बनाता प्रतीत होता है, और उच्चतम मूल्य के साथ वर्ग के साथ उद्देश्य।

आप मेरे परिणाम यहाँ लिंक विवरण दर्ज करके देख सकते हैं


क्या आप शायद अपने उत्तर और विशेष रूप से अपनी छवियों और लिंक को ठीक कर सकते हैं?
बार्ट

-2

"युद्धपोट" जिसे एक क्लासिक कंप्यूटर विज्ञान एनपी-पूर्ण समस्या के रूप में जाना जाता है।

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

(बैटलशिप के लिए देखो - यह वहाँ है, खेल और पहेली के तहत)


4
जो एक युद्धपोत पहेली ( en.wikipedia.org/wiki/Battleship_(puzzle) ) है, न कि बैटलशिप गेम ( en.wikipedia.org/wiki/Battleship_(game) )।
जेसन बर्कन

हाँ, जैसा कि जेसन ने कहा कि यह एक पूरी तरह से अलग जानवर है।
जॉन गित्जन

3
Hehehe। अगला असाइनमेंट मुझे काम पर मिलता है मैं यह कहने जा रहा हूं कि यह एनपी-पूर्ण है, फिर एक लंबा लंच लें। :-)
बोर्क ब्लाट 12
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.