Splix.io - ราชาแห่งแผ่นดิน


37

คุณเป็นจุดกล้าได้กล้าเสียที่ต้องการเพิ่มดินแดนภายใต้การควบคุมของมัน นี่ค่อนข้างง่าย - เดินทางนอกดินแดนปัจจุบันของคุณแล้ววนกลับเข้ามาในดินแดนของคุณและทุกสิ่งในวงนั้นตอนนี้คุณเป็นเจ้าของแล้ว แต่มีการจับ หากจุดอื่นพบจุดวนของคุณแล้วข้ามไปคุณจะตาย

หากคุณยังไม่ได้ลองใช้ให้ไปที่Splix.ioแล้วลองเกม ใช้ปุ่มลูกศรเพื่อควบคุมการเคลื่อนไหวของคุณ

GIF

ป้อนคำอธิบายรูปภาพที่นี่

เครดิต: http://splix.io/

ข้อมูลจำเพาะ

ผู้เล่นทุกคนเริ่มต้นที่ตำแหน่งสุ่มบนกระดาน 200x200 (ฉันขอสงวนสิทธิ์ในการเปลี่ยนแปลงนี้ :) คุณจะมีการเคลื่อนไหวจำนวนหนึ่งเพื่อสะสมคะแนนมากที่สุดเท่าที่จะเป็นไปได้ คะแนนจะนับโดย:

  • จำนวนผู้เล่นที่คุณฆ่าถึง 300 ครั้ง
  • จำนวนที่ดินที่คุณเป็นเจ้าของเมื่อสิ้นสุดรอบ

นี่นำมาซึ่งจุดที่คนอื่นสามารถขโมยที่ดินของคุณได้ หากพวกเขาเริ่มวนรอบที่ตัดที่ดินของคุณพวกเขาสามารถอ้างสิทธิ์ได้ หากคุณตายในระหว่างรอบคุณสูญเสียคะแนนทั้งหมดสำหรับรอบนั้น

แต่ละรอบมีกลุ่มผู้เล่นที่เลือกแบบสุ่ม (สูงสุด 5 ผู้เล่นที่ไม่ซ้ำกัน) (อาจมีการเปลี่ยนแปลง) ผู้เล่นทุกคนมีส่วนร่วมในจำนวนรอบเท่ากัน คะแนนสุดท้ายของบอทของคุณถูกกำหนดโดยคะแนนเฉลี่ยต่อเกม แต่ละเกมประกอบด้วย 2,000 รอบ (อาจมีการเปลี่ยนแปลง) บอตทั้งหมดเคลื่อนไหวในเวลาเดียวกัน

คดีความ

หัวชน

ก้นหัว

ผู้เล่นทั้งสองตายเมื่อพวกเขาหัวชนกัน สิ่งนี้ยังคงเป็นจริงแม้ว่าผู้เล่นทั้งสองจะอยู่ในพื้นที่ของพวกเขา

ก้นหัว

อย่างไรก็ตามเมื่อมีผู้เล่นเพียงคนเดียวที่อยู่ในดินแดนของเขาผู้เล่นคนอื่นจะตาย

ป้อนคำอธิบายรูปภาพที่นี่

สายไขว้

ป้อนคำอธิบายรูปภาพที่นี่

ในกรณีนี้มีเพียงผู้เล่นสีม่วงที่เสียชีวิต

คุณไม่สามารถข้ามเส้นของคุณเอง

ป้อนคำอธิบายรูปภาพที่นี่

ออกจากบอร์ด

ผู้เล่นออกจากบอร์ด

หากผู้เล่นพยายามออกจากกระดานเขาจะตายและเสียคะแนนทั้งหมด

จับภาพพื้นที่

ผู้เล่นจะยึดพื้นที่เมื่อเขามีเส้นทางและเขาเข้าสู่ดินแดนของตัวเองอีกครั้ง

ป้อนคำอธิบายรูปภาพที่นี่

สีแดงเติมระหว่างเส้นสองเส้นสีแดง กรณีเดียวที่ผู้เล่นไม่กรอกคือเมื่อผู้เล่นคนอื่นอยู่ในวง เพื่อความชัดเจนสิ่งนี้จะใช้เฉพาะเมื่อผู้เล่นคนอื่นอยู่ในวงเท่านั้นไม่ใช่แค่เจ้าของที่ดินเท่านั้น ผู้เล่นสามารถยึดครองดินแดนจากบุคคลอื่นได้ หากผู้เล่นไม่สามารถเติมเต็มพื้นที่ที่ล้อมรอบด้วยเส้นทางของเขาเส้นทางจะถูกแปลงเป็นดินแดนปกติโดยตรง หากผู้เล่นในวงผู้เล่นอื่นลงจอดพื้นที่ในวงนั้นจะเต็ม ทุกครั้งที่ผู้เล่นเสียชีวิตบอร์ดจะได้รับการตรวจสอบอีกครั้งสำหรับพื้นที่ที่สามารถเติมได้

รายละเอียดคอนโทรลเลอร์

ตัวควบคุมเป็นที่นี่ มันคล้ายกับเกมดั้งเดิม แต่มีการเปลี่ยนแปลงเล็กน้อยเพื่อให้เหมาะกับ KotH และด้วยเหตุผลทางเทคนิค มันถูกสร้างขึ้นด้วย@NathanMerrill 's ห้องสมุด KotHCommและด้วยความช่วยเหลืออย่างมากจาก @NathanMerrill เช่นกัน กรุณาแจ้งให้เราทราบจากข้อบกพร่องใด ๆ ที่คุณพบในการควบคุมในห้องสนทนา เพื่อให้สอดคล้องกับ KotHComm ฉันได้ใช้คอลเลกชัน Eclipse ทั่วคอนโทรลเลอร์ แต่สามารถเขียนบอทได้โดยใช้ไลบรารี Java Collections เท่านั้น

ทุกอย่างจะถูกบรรจุใน uberjar บนหน้า GitHub เผยแพร่ เมื่อต้องการใช้ให้ดาวน์โหลดและแนบกับโครงการของคุณเพื่อให้คุณสามารถใช้สำหรับการคอมไพล์อัตโนมัติ (คำแนะนำสำหรับIntelliJ , Eclipse ) ในการทดสอบการส่งของคุณ, java -jar SplixKoTH-all.jar -d path\to\submissions\folderคุณเรียกใช้ขวดด้วย ตรวจสอบให้แน่ใจว่าpath\to\submissions\folderมี subfoler ชื่อjavaและวางไฟล์ทั้งหมดของคุณที่นั่น อย่าใช้ชื่อแพ็คเกจในบอตของคุณ (แม้ว่า KotHComm อาจจะเป็นไปได้ แต่ก็เป็นปัญหาอีกเล็กน้อย) --helpหากต้องการดูตัวเลือกทั้งหมดที่ใช้ --question-id 126815จะโหลดทั้งหมดบอทใช้

การเขียนบอท

SplixPlayerในการเริ่มต้นการเขียนบอทคุณต้องขยาย

  • Direction makeMove(ReadOnlyGame game, ReadOnlyBoard board)
    • ที่นี่เป็นที่ที่คุณตัดสินใจว่าต้องการย้ายบอทของคุณไปไหน ต้องไม่ส่งคืน null
  • HiddenPlayer getThisHidden()
    • รับรุ่นHiddenPlayer thisมีประโยชน์สำหรับการเปรียบเทียบบอตของคุณกับบอร์ด

enum Direction

  • ค่า
    • East (x = 1; y = 0)
    • West (x = -1; y = 0)
    • North (x = 0; y = 1)
    • South (x = 0; y = -1)
  • Direction leftTurn()
    • รับสิ่งDirectionที่คุณจะได้รับหากคุณเลี้ยวซ้าย
  • Direction RightTurn()
    • รับสิ่งDirectionที่คุณจะได้รับหากคุณเลี้ยวขวา

ReadOnlyBoard

นี่คือคลาสที่คุณเข้าถึงบอร์ด คุณสามารถรับมุมมองท้องถิ่น (20x20) ของบอร์ดโดยแสดงตำแหน่งผู้เล่นหรือมุมมองทั่วโลก (ทั้งบอร์ด) โดยมีเฉพาะข้อมูลของผู้ที่เป็นเจ้าของและอ้างสิทธิ์ตำแหน่งบนกระดาน นี่คือที่ที่คุณได้รับตำแหน่งของคุณ

  • SquareRegion getBounds()
    • ดึงขนาดของบอร์ด
  • MutableMap<com.nmerrill.kothcomm.game.maps.Point2D,ReadOnlySplixPoint> getGlobal()
    • รับแผนที่ทั่วโลกของคณะกรรมการ
  • MutableMap<com.nmerrill.kothcomm.game.maps.Point2D,ReadOnlySplixPoint> getView()
    • เช่นเดียวกับgetGlobal()ยกเว้นว่ามันถูก จำกัด ไว้ที่พื้นที่ 20x20 รอบเครื่องเล่นของคุณและแสดงตำแหน่งของผู้เล่น
  • Point2D getPosition(SplixPlayer me)
    • รับตำแหน่งผู้เล่นของคุณ board.getPosition(this)ใช้เป็น
  • Point2D getSelfPosition(ReadOnlyBoard)
    • รับตำแหน่งของคุณบนกระดาน การใช้งาน:Point2D mypos = getSelfPosition(board)

ReadOnlyGame

ReadOnlyGameให้การเข้าถึงจำนวนการเลี้ยวที่เหลือในเกมผ่านint getRemainingIterations()เท่านั้น

ReadOnlySplixPoint

  • HiddenPlayer getClaimer()
    • รับHiddenPlayerรุ่นของผู้ที่อ้างสิทธิ์จุด - การอ้างสิทธิ์ = เส้นทาง
  • HiddenPlayer getOwner()
    • รับผู้ที่เป็นเจ้าของจุด
  • HiddenPlayer getWhosOnSpot()
    • หากผู้เล่นอยู่ในตำแหน่งนี้ให้คืนเวอร์ชั่นที่ซ่อนไว้ getLocal()จะทำงานเฉพาะใน

Point2D

ไม่เหมือนกับคลาสอื่น ๆ ที่นี่Point2Dมีอยู่ในไลบรารี KotHCommcom.nmerrill.kothcomm.game.maps.Point2D

  • Point2D(int x, int y)
  • int getX()
  • int getY()
  • Point2D moveX(int x)
  • Point2D moveY(int y)
  • Point2D wrapX(int maxX)
    • ห่อคุ้มค่าที่จะอยู่ในช่วงของxmaxX
  • Point2D wrapY(int maxY)
    • ห่อคุ้มค่าที่จะอยู่ในช่วงของymaxY
  • int cartesianDistance(Point2D other)
    • สิ่งนี้แปลเป็นจำนวนรอบที่ผู้เล่นต้องย้ายจากจุดหนึ่งไปยังอีกจุดหนึ่ง

การสนับสนุน Clojure

คอมไพเลอร์ Clojure มาพร้อมกับSplixKoTH-all.jarดังนั้นคุณสามารถใช้ Clojure สำหรับบอทของคุณ! อ้างอิงถึงของฉันrandom_botเพื่อดูวิธีการใช้งาน

การดีบักบ็อต

คอนโทรลเลอร์มาพร้อมกับดีบักเกอร์เพื่อช่วยในการทดสอบกลยุทธ์ ในการเริ่มต้นให้เรียกใช้ jar ด้วย--guiตัวเลือก

ในการแนบตัวดีบั๊กกับ jar ของคุณให้ทำตามคำแนะนำเหล่านี้สำหรับ IntelliJ หรือคำแนะนำเหล่านี้สำหรับ Eclipse (ไม่ได้ทดสอบเวอร์ชัน Eclipse)

ป้อนคำอธิบายรูปภาพที่นี่

หากคุณกำลังใช้ดีบักเกอร์กับรหัสของคุณคุณสามารถใช้สิ่งนี้เพื่อช่วยให้เห็นภาพสิ่งที่บอทของคุณเห็น ตั้งค่าเบรกพอยต์ที่จุดเริ่มต้นของmakeMoveบน bot ของคุณและตรวจสอบให้แน่ใจว่ามันหยุดเฉพาะเธรดปัจจุบันเท่านั้น จากนั้นคลิกปุ่มเริ่มบน UI และทำตามรหัสของคุณ

ป้อนคำอธิบายรูปภาพที่นี่

ตอนนี้เพื่อรวมทั้งหมด:

วิ่งบอท

ในการรันบ็อตของคุณกับผู้อื่นคุณต้องรัน jar บนหน้าเผยแพร่ นี่คือรายการของธง:

  • --iterations( -i) <= int(ค่าเริ่มต้น500)
    • ระบุจำนวนเกมที่จะเรียกใช้
  • --test-bot( -t) <=String
    • เรียกใช้เฉพาะเกมที่ bot รวมอยู่ด้วย
  • --directory( -d) <= เส้นทาง
    • ไดเรกทอรีเพื่อเรียกใช้การส่งจาก ใช้สิ่งนี้เพื่อเรียกใช้บอทของคุณ javaตรวจสอบให้แน่ใจว่าบอทของคุณอยู่ในโฟลเดอร์ย่อยของเส้นทางที่ชื่อว่า
  • --question-id( -q) <= int(ใช้เฉพาะ126815)
    • ดาวน์โหลดและรวบรวมผลงานอื่น ๆ จากเว็บไซต์
  • --random-seed( -r) <= int(ค่าเริ่มต้นเป็นตัวเลขสุ่ม)
    • ให้เมล็ดแก่นักวิ่งเพื่อให้บอทที่ใช้การสุ่มสามารถสร้างผลลัพธ์ได้
  • --gui( -g)
    • เรียกใช้ debugger UI แทนการเรียกใช้ทัวร์นาเมนต์ --test-botที่ดีที่สุดมาใช้กับ
  • --multi-thread( -m) <= boolean(ค่าเริ่มต้นtrue)
    • เรียกใช้ tournoment ในโหมดมัลติเธรด สิ่งนี้จะช่วยให้ได้ผลลัพธ์ที่เร็วขึ้นหากคอมพิวเตอร์ของคุณมีหลายคอร์
  • --thread-count( -c) <= int(ค่าเริ่มต้น4)
    • จำนวนเธรดที่จะรันหากอนุญาตให้มีหลายเธรด
  • --help( -h)
    • พิมพ์ข้อความช่วยเหลือคล้ายกับสิ่งนี้

java -jar SplixKoTH-all.jar -q 126815เมื่อต้องการเรียกใช้ส่งเข้ามาทั้งหมดในหน้านี้ใช้

การจัดรูปแบบโพสต์ของคุณ

เพื่อให้แน่ใจว่าคอนโทรลเลอร์สามารถดาวน์โหลดบ็อตทั้งหมดได้คุณควรทำตามรูปแบบนี้

[BotName], Java                     // this is a header
                                    // any explanation you want
[BotName].java                      // filename, in the codeblock
[code]

นอกจากนี้อย่าใช้การประกาศแพ็คเกจ


ป้ายบอกคะแนน

+------+--------------+-----------+
| Rank | Name         |     Score |
+------+--------------+-----------+
|    1 | ImNotACoward | 8940444.0 |
|    2 | TrapBot      |  257328.0 |
|    3 | HunterBot    |  218382.0 |
+------+--------------+-----------+

กรุณาแจ้งให้เราทราบว่าส่วนใดส่วนหนึ่งของกฎมีความชัดเจนหรือถ้าคุณพบข้อผิดพลาดในการควบคุมในห้องแชท

มีความสุข!


เฮ้ในที่สุดนี้ก็โพสต์! ฉันสงสัย: D
MD XF

นานแค่ไหนที่คุณได้รับการรอคอย? ;) คุณวางแผนที่จะส่งหรือไม่?
J Atkin

ฉันไม่รู้ว่าฉันจะสามารถแก้ปัญหาแบบนี้ได้หรือไม่เพราะฉันเขียนโปรแกรมเป็นภาษา esolangs เป็นหลัก แต่ฉันเห็นมันในกล่องทรายและมันก็เป็นความท้าทายที่ยิ่งใหญ่!
MD XF

@hyperneutrino ฉันเห็นการแก้ไขมันรบกวนคุณจริงๆเหรอ? ความถูกต้องทางการเมืองไม่ได้มีอยู่ในขอบเขตของโพสต์นี้และมันเป็นไวยากรณ์ภาษาอังกฤษที่ถูกต้องสมบูรณ์แบบ
J Atkin

2
0.o โลกใบเล็ก? ฉันรู้ว่า (จาก) ผู้พัฒนาของ splix.io (รีทวีต @ เขานี้)
CAD97

คำตอบ:


2

ImNotACoward, Java

บอทนี้เป็นผู้เชี่ยวชาญด้านการเอาชีวิตรอดที่ขี้ขลาด หากไม่มีศัตรูใกล้เคียงเขาอ้างว่าเป็นส่วนหนึ่งของแผ่นดิน หากห่วงของผู้เล่นอื่นสามารถเข้าถึงได้อย่างปลอดภัยเขาแทงผู้เล่นคนอื่นที่ด้านหลังเข้าร่วมผู้เล่นคนอื่นด้วยการดวล หากผู้เล่นคนอื่นไม่สามารถโจมตีได้อย่างปลอดภัยเขาจะหนีไปทำการล่าถอยเชิงกลยุทธ์ไปยังดินแดนของเขาเอง

ImNotACoward.java
import org.eclipse.collections.api.map.MutableMap;
import org.eclipse.collections.api.set.MutableSet;
import org.eclipse.collections.impl.factory.Lists;
import org.eclipse.collections.impl.factory.Maps;
import org.eclipse.collections.impl.factory.Sets;

import com.jatkin.splixkoth.ppcg.game.Direction;
import com.jatkin.splixkoth.ppcg.game.SplixPlayer;
import com.jatkin.splixkoth.ppcg.game.readonly.HiddenPlayer;
import com.jatkin.splixkoth.ppcg.game.readonly.ReadOnlyBoard;
import com.jatkin.splixkoth.ppcg.game.readonly.ReadOnlyGame;
import com.jatkin.splixkoth.ppcg.game.readonly.ReadOnlySplixPoint;
import com.nmerrill.kothcomm.game.maps.Point2D;
import com.nmerrill.kothcomm.game.maps.graphmaps.bounds.point2D.SquareRegion;

public class ImNotACoward extends SplixPlayer {
    private static final MutableSet<Direction> DIRECTIONS = Sets.mutable.of(Direction.values());

    private static class Board {
        public MutableSet<Point2D> allPoints = null;
        private SquareRegion globalBounds = null;
        private SquareRegion viewBounds = null;
        private MutableMap<Point2D, ReadOnlySplixPoint> global = null;
        private MutableMap<Point2D, ReadOnlySplixPoint> view = null;

        public void update(ReadOnlyBoard readOnlyBoard) {
            if (this.allPoints == null) {
                this.allPoints = readOnlyBoard.getGlobal().keysView().toSet();
                this.globalBounds = readOnlyBoard.getBounds();
            }
            this.viewBounds = readOnlyBoard.viewingArea;
            this.global = readOnlyBoard.getGlobal();
            this.view = readOnlyBoard.getView();
        }

        public boolean inBounds(Point2D point) {
            return globalBounds.inBounds(point);
        }

        public boolean inView(Point2D point) {
            return viewBounds.inBounds(point);
        }

        public ReadOnlySplixPoint getSplixPoint(Point2D point) {
            return inView(point) ? view.get(point) : global.get(point);
        }

        public MutableSet<Point2D> getNeighbors(Point2D point) {
            return DIRECTIONS.collect(d -> point.move(d.vector.getX(), d.vector.getY())).select(this::inBounds);
        }

        public MutableSet<Point2D> getNeighbors(MutableSet<Point2D> points) {
            return points.flatCollect(this::getNeighbors);
        }

        public MutableSet<Point2D> getBorders(SquareRegion region) {
            return allPoints.select(p -> region.inBounds(p) &&
                    (p.getX() == region.getLeft() || p.getX() == region.getRight() ||
                    p.getY() == region.getTop() || p.getY() == region.getBottom() ||
                    p.getX() == globalBounds.getLeft() || p.getX() == globalBounds.getRight() ||
                    p.getY() == globalBounds.getTop() || p.getY() == globalBounds.getBottom()));
        }
    }

    private class Player {
        public final HiddenPlayer hiddenPlayer;
        public MutableSet<Point2D> owned = Sets.mutable.empty();
        private MutableSet<Point2D> unowned = null;
        private MutableSet<Point2D> oldClaimed = Sets.mutable.empty();
        public MutableSet<Point2D> claimed = Sets.mutable.empty();
        private MutableSet<Point2D> oldPos = Sets.mutable.empty();
        public MutableSet<Point2D> pos = Sets.mutable.empty();

        public Player(HiddenPlayer hiddenPlayer) {
            super();
            this.hiddenPlayer = hiddenPlayer;
        }

        public void nextMove() {
            owned.clear();
            unowned = null;
            oldClaimed = claimed;
            claimed = Sets.mutable.empty();
            oldPos = pos;
            pos = Sets.mutable.empty();
        }

        public MutableSet<Point2D> getUnowned() {
            if (unowned == null) {
                unowned = board.allPoints.difference(owned);
            }
            return unowned;
        }

        public void addOwned(Point2D point) {
            owned.add(point);
        }

        public void addClaimed(Point2D point) {
            claimed.add(point);
        }

        public void setPos(Point2D point) {
            pos.clear();
            pos.add(point);
        }

        public void calcPos() {
            if (pos.isEmpty()) {
                MutableSet<Point2D> claimedDiff = claimed.difference(oldClaimed);
                if (claimedDiff.size() == 1) {
                    pos = board.getNeighbors(claimedDiff).select(p -> !claimed.contains(p) && !board.inView(p));
                } else if (!oldPos.isEmpty()) {
                    pos = board.getNeighbors(oldPos).select(p -> owned.contains(p) && !board.inView(p));
                } else {
                    pos = owned.select(p -> !board.inView(p));
                }
            }
        }
    }

    private Board board = new Board();
    private Point2D myPos = null;
    private final Player nobody = new Player(new HiddenPlayer(null));
    private final Player me = new Player(new HiddenPlayer(this));
    private MutableMap<HiddenPlayer, Player> enemies = Maps.mutable.empty();
    private MutableMap<HiddenPlayer, Player> players = Maps.mutable.of(nobody.hiddenPlayer, nobody, me.hiddenPlayer, me);
    private MutableSet<Point2D> path = Sets.mutable.empty();

    private Player getPlayer(HiddenPlayer hiddenPlayer) {
        Player player = players.get(hiddenPlayer);
        if (player == null) {
            player = new Player(hiddenPlayer);
            players.put(player.hiddenPlayer, player);
            enemies.put(player.hiddenPlayer, player);
        }
        return player;
    }

    private Direction moveToOwned() {
        MutableSet<Point2D> targets = me.owned.difference(me.pos);
        if (targets.isEmpty()) {
            return moveTo(myPos);
        } else {
            return moveTo(targets.minBy(myPos::cartesianDistance));
        }
    }

    private Direction moveTo(Point2D target) {
        return DIRECTIONS.minBy(d -> {
            Point2D p = myPos.move(d.vector.getX(), d.vector.getY());
            return !board.inBounds(p) || me.claimed.contains(p) ? Integer.MAX_VALUE : target.cartesianDistance(p);
        });
    }

    @Override
    protected Direction makeMove(ReadOnlyGame readOnlyGame, ReadOnlyBoard readOnlyBoard) {
        board.update(readOnlyBoard);
        myPos = readOnlyBoard.getPosition(this);
        path.remove(myPos);

        for (Player e : players.valuesView()) {
            e.nextMove();
        }
        for (Point2D point : board.allPoints) {
            ReadOnlySplixPoint splixPoint = board.getSplixPoint(point);
            getPlayer(splixPoint.getOwner()).addOwned(point);
            getPlayer(splixPoint.getClaimer()).addClaimed(point);
            getPlayer(splixPoint.getWhosOnSpot()).setPos(point);
        }
        for (Player e : players.valuesView()) {
            e.calcPos();
        }

        if (me.owned.contains(myPos) && path.allSatisfy(p -> me.owned.contains(p))) {
            path.clear();
        }

        if (path.isEmpty()) {
            MutableSet<Point2D> enemyPositions = enemies.valuesView().flatCollect(e -> e.pos).toSet();
            int enemyDistance = enemyPositions.isEmpty() ? Integer.MAX_VALUE :
                    enemyPositions.minBy(myPos::cartesianDistance).cartesianDistance(myPos);

            if (enemyDistance < 20) {
                MutableSet<Point2D> enemyClaimed = enemies.valuesView().flatCollect(e -> e.claimed).toSet();
                if (!enemyClaimed.isEmpty()) {
                    Point2D closestClaimed = enemyClaimed.minBy(myPos::cartesianDistance);
                    if (closestClaimed.cartesianDistance(myPos) < enemyDistance) {
                        return moveTo(closestClaimed);
                    } else if (enemyDistance < 10) {
                        return moveToOwned();
                    }
                }
            }

            if (me.owned.contains(myPos)) {
                if (!me.getUnowned().isEmpty()) {
                    Point2D target = me.getUnowned().minBy(myPos::cartesianDistance);
                    if (target.cartesianDistance(myPos) > 2) {
                        return moveTo(target);
                    }
                }

                int safeSize = Math.max(1, Math.min(enemyDistance / 6, readOnlyGame.getRemainingIterations() / 4));
                SquareRegion region = Lists.mutable
                        .of(new SquareRegion(myPos, myPos.move(safeSize, safeSize)),
                                new SquareRegion(myPos, myPos.move(safeSize, -safeSize)),
                                new SquareRegion(myPos, myPos.move(-safeSize, safeSize)),
                                new SquareRegion(myPos, myPos.move(-safeSize, -safeSize)))
                        .maxBy(r -> me.getUnowned().count(p -> r.inBounds(p)));
                path = board.getBorders(region);
            } else {
                return moveToOwned();
            }
        }

        if (!path.isEmpty()) {
            return moveTo(path.minBy(myPos::cartesianDistance));
        }

        return moveToOwned();
    }
}

น่าสนใจ ดีมาก! ฉันสงสัยว่าดีขึ้นมากนี้อาจจะทำ ...
J Atkin

1

TrapBot, Java

TrapBot.java

import com.jatkin.splixkoth.ppcg.game.Direction;
import com.jatkin.splixkoth.ppcg.game.SplixPlayer;
import com.jatkin.splixkoth.ppcg.game.readonly.ReadOnlyBoard;
import com.jatkin.splixkoth.ppcg.game.readonly.ReadOnlyGame;
import com.jatkin.splixkoth.ppcg.game.readonly.ReadOnlySplixPoint;
import com.nmerrill.kothcomm.game.maps.Point2D;
import com.nmerrill.kothcomm.game.maps.graphmaps.bounds.point2D.SquareRegion;
import javafx.util.Pair;
import org.eclipse.collections.api.map.MutableMap;
import org.eclipse.collections.impl.factory.Lists;

import java.util.Comparator;

/**
 * Trap bot goes to the wall and traces the entirety around. Hopes that
 * the players in the middle die and that nobody challenges him. Nearly 
 * all turns are left turns.
 */
public class TrapBot extends SplixPlayer {

    /**
     * Mode when the bot is attempting to reach the wall from it's original spawn
     * location.
     */
    public static final int MODE_GOING_TO_WALL = 1;

    /**
     * Mode when we have reached the wall and are now going around the board.
     */
    public static final int MODE_FOLLOWING_WALL = 2;

    private int mode = MODE_GOING_TO_WALL;

    public static int WALL_EAST = 1;
    public static int WALL_NORTH = 2;
    public static int WALL_WEST = 3;
    public static int WALL_SOUTH = 4;


    /**
     * How long the bot would like to go before he turns around to go back home.
     */
    private static final int PREFERRED_LINE_DIST = 5;

    private int distToTravel = 0;

    private Direction lastMove = Direction.East;// could be anything that's not null
    private int lastTrailLength = 0;

    @Override
    protected Direction makeMove(ReadOnlyGame game, ReadOnlyBoard board) {
        Direction ret = null;
        MutableMap<Point2D, ReadOnlySplixPoint> view = board.getView();
        int trailLength = getTrailLength(board, view);

        if (trailLength == 0) {

            int closestWall = getClosestWall(board);
            Direction directionToWall = getDirectionToWall(closestWall);

            if (lastTrailLength != 0) {
                ret = lastMove.leftTurn();
                // move to the other half of 2 width line so we can start without shifting to the left
            }

            if (mode == MODE_GOING_TO_WALL && ret == null) {
                int distCanTravel = getDistCanTravel(
                        getSelfPosition(board), board.getBounds(), directionToWall);
                if (distCanTravel == 0) mode = MODE_FOLLOWING_WALL;
                else ret = directionToWall;
                distToTravel = distCanTravel;

            }

            if (mode == MODE_FOLLOWING_WALL && ret == null) {
                int distCanTravel = 0;
                ret = directionToWall;
                while (distCanTravel == 0) {// keep turning left until we can get somewhere
                    ret = ret.leftTurn();
                    distCanTravel = getDistCanTravel(
                            getSelfPosition(board), board.getBounds(), ret);
                }

                distToTravel = distCanTravel;
            }
        }

        // once we have started we are on auto pilot (can't run after the before block)
        else if (trailLength == distToTravel || trailLength == (distToTravel + 1))
            ret = lastMove.leftTurn();

        if (ret == null)// if we don't have a move otherwise, we must be on our trail. ret same as last time
            ret = lastMove;

        lastTrailLength = trailLength;
        lastMove = ret;
        return ret;
    }

    int getClosestWall(ReadOnlyBoard board) {
        Point2D thisPos = getSelfPosition(board);
        return Lists.mutable.of(
                new Pair<>(WALL_NORTH, board.getBounds().getTop() - thisPos.getY()),
                new Pair<>(WALL_SOUTH, thisPos.getY()), 
                new Pair<>(WALL_EAST, board.getBounds().getRight() - thisPos.getX()),
                new Pair<>(WALL_WEST, thisPos.getX())
        ).min(Comparator.comparingInt(Pair::getValue)).getKey();
    }

    /**
     * This goes around some intended behavior in the controller to get the correct result. When a player goes outside
     * his territory the land under him is converted to a trail -- on the next step of the game. So a trail length may
     * be the count of the trail locations plus one. That is what this function calculates. Depends on the whole trail
     * being contained inside the view passed to it.
     * @return
     */
    int getTrailLength(ReadOnlyBoard board, MutableMap<Point2D, ReadOnlySplixPoint> view) {
        boolean isPlayerOutsideHome = !view.get(getSelfPosition(board)).getOwner().equals(getThisHidden());
        int trailLength = view.count(rop -> rop.getClaimer().equals(getThisHidden()));
        return trailLength + (isPlayerOutsideHome? 1 : 0);
    }

    /**
     * Calculate how far we can travel in the direction before we hit the wall.
     * @return
     */
    int getDistCanTravel(Point2D currPos, SquareRegion bounds, Direction direction) {
        for (int i = 1; i <= PREFERRED_LINE_DIST; i++) {
            if (!bounds.inBounds(currPos.move(direction.vector.getX()*i, direction.vector.getY()*i)))
                return i-1;
        }
        return PREFERRED_LINE_DIST;
    }

    /**
     * Get which direction needs to be traveled to reach the specified wall.
     * Requires that neither Direction nor the values of `WALL_...` change.
     * @param targetWall
     * @return
     */
    Direction getDirectionToWall(int targetWall) {
        return Direction.values()[targetWall-1];
    }
}

นี่อาจเป็นบอทที่ง่ายที่สุด สิ่งที่มันทำคือไล่ตามขอบของบอร์ดออกไปสองเท่าเพื่อลดความเสี่ยงที่จะถูกฆ่า


เยี่ยมมากที่จะเห็นว่าคุณใช้ Eclipse Collections มีอินเตอร์เฟสเป็นคู่ใน EC คุณสามารถใช้ Tuples.pair () เพื่อรับอินสแตนซ์ของคู่ นอกจากนี้ยังมีคลาส PrimitiveTuples หากค่าใดค่าหนึ่งหรือทั้งคู่ในคู่นั้นเป็นค่าพื้นฐาน
Donald Raab

1

random_bot, Clojure

นี่คือRandomBotแต่ฉันต้องติดกับการตั้งชื่อการประชุมและปัญหาบางอย่างทำให้ฉันไม่สามารถใช้ยัติภังค์ในชื่อเพื่อขีดเส้นใต้! make-moveFn ผลตอบแทน vec กับรายการแรกเป็นDirectionคุณต้องการที่จะย้ายเข้ามาอยู่และที่สองเป็นรัฐที่คุณต้องการที่จะส่งกลับไปยังคุณในทางกลับกันต่อไป อย่าใช้อะตอมภายนอกเนื่องจากรหัสนี้อาจใช้หลายเกมพร้อมกัน

 random_bot.clj
 (ns random-bot
     (:import
      [com.jatkin.splixkoth.ppcg.game Direction]))

 (defn make-move [game board state]
       [(rand-nth [Direction/East
                   Direction/West
                   Direction/North
                   Direction/South])
        nil])

0

HunterBot, Java

HunterBot.java

import com.jatkin.splixkoth.ppcg.game.Direction;
import com.jatkin.splixkoth.ppcg.game.SplixPlayer;
import com.jatkin.splixkoth.ppcg.game.readonly.HiddenPlayer;
import com.jatkin.splixkoth.ppcg.game.readonly.ReadOnlyBoard;
import com.jatkin.splixkoth.ppcg.game.readonly.ReadOnlyGame;
import com.jatkin.splixkoth.ppcg.game.readonly.ReadOnlySplixPoint;
import com.nmerrill.kothcomm.game.maps.Point2D;
import org.eclipse.collections.api.map.MutableMap;
import org.eclipse.collections.api.set.ImmutableSet;
import org.eclipse.collections.impl.factory.Sets;

import java.util.Comparator;

/**
 * This bot looks for any trail points left behind by another player and sets that as his target. If the target ever
 * disappears, it will continue on in hopes that the player will return soon, or if another target appears, it will
 * go towards that one. Works best when the other player repeatedly goes in the same general direction.
 */
public class HunterBot extends SplixPlayer {

    private Point2D lastTarget;

    private Direction lastMove = Direction.East;

    @Override
    protected Direction makeMove(ReadOnlyGame game, ReadOnlyBoard board) {
        Point2D thisPos = getSelfPosition(board);
        MutableMap<Point2D, ReadOnlySplixPoint> global = board.getGlobal();
        MutableMap<Point2D, ReadOnlySplixPoint> targets = global.select((pt, rosp) ->
                !rosp.getClaimer().equals(getThisHidden()) 
                        && !rosp.getClaimer().equals(new HiddenPlayer(null)));

        if (targets.size() == 0 && lastTarget == null) {
            lastMove = lastMove.leftTurn();
            return lastMove;
        }

        Point2D target = null;
        if (targets.size() == 0) target = lastTarget;
        else target = targets.keysView().min(Comparator.comparingInt(thisPos::cartesianDistance));
        if (target.equals(thisPos)) {
            lastTarget = null;
            if (global.get(thisPos).getOwner().equals(getThisHidden())) {
                lastMove = lastMove.leftTurn();
                return lastMove;
            } else 
            // time to go home
            target = global.select((z_, x) -> getThisHidden().equals(x.getOwner())).keySet().iterator().next();

        }

        lastTarget = target;
        lastMove = makeSafeMove(target, global, board, thisPos);
        return lastMove;
    }

    private Direction makeSafeMove(Point2D targetLocation, MutableMap<Point2D, ReadOnlySplixPoint> map, ReadOnlyBoard board, Point2D currLoc) {
        Point2D dist = targetLocation.move(-currLoc.getX(), -currLoc.getY());
        ImmutableSet<Direction> possibleMoves = Sets.immutable.of(Direction.values())
                .select(x -> {
                    Point2D pos = currLoc.move(x.vector.getX(), x.vector.getY());
                    return !board.getBounds().outOfBounds(pos) && !getThisHidden().equals(map.get(pos).getClaimer());
                });
        Direction prefMove;
        if (Math.abs(dist.getX()) > Math.abs(dist.getY()))
            prefMove = getDirectionFroPoint(new Point2D(normalizeNum(dist.getX()), 0));
        else
            prefMove = getDirectionFroPoint(new Point2D(0, normalizeNum(dist.getY())));

        if (possibleMoves.contains(prefMove)) return prefMove;
        if (possibleMoves.contains(prefMove.leftTurn())) return prefMove.leftTurn();
        if (possibleMoves.contains(prefMove.rightTurn())) return prefMove.rightTurn();
        return prefMove.leftTurn().leftTurn();
    }

    private Direction getDirectionFroPoint(Point2D dir) {
        return Sets.immutable.of(Direction.values()).select(d -> d.vector.equals(dir)).getOnly();
    }

    private int normalizeNum(int n) { if (n < -1) return -1; if (n > 1) return 1; else return n;}

}

หนึ่งในบอทพื้นฐานที่สุด มันจะค้นหากระดานเพื่อหาจุดที่จะฆ่าคนอื่นและมันจะไปตามเส้นทางที่สั้นที่สุดที่เป็นไปได้เพื่อไปยังตำแหน่งฆ่า หากอยู่นอกอาณาเขตของตนมันจะทำการสุ่มย้ายจนกว่าจะมีการเปิดอีกครั้งเพื่อฆ่าผู้เล่นอื่น มันมีเหตุผลบางอย่างที่จะป้องกันไม่ให้มันวิ่งข้ามตัวเองและเมื่อผู้เล่นคนอื่นตายมันก็กลับไปที่บ้าน เมื่อกลับบ้านมันจะไปในสี่เหลี่ยมเล็ก ๆ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.