Scriptbot Warz!


14

Scriptbot Warz!


ผลลัพธ์อยู่ในและAssassinเป็นแชมป์ของเราชนะ 2 จาก 3 แมทช์! ขอบคุณทุกคนที่ส่ง Scriptbots! ขอขอบคุณเป็นพิเศษสำหรับแตรสำหรับBestOpportunityBotซึ่งแสดงเส้นทางที่ยอดเยี่ยมและใช้ประโยชน์จากตัวเลือกการดำเนินการทั้งหมด

แผนที่ 1

แอสแซสซินดึงเอาโอกาสที่ดีที่สุดออกมาก่อนและการแข่งขันที่เหลือก็ค่อนข้างน่าเบื่อ รายละเอียดการเล่นโดยการเล่นที่นี่

  1. Assassin: 10 HP, 10 Dealt ความเสียหาย, 3 ดาเมจที่ได้รับ
  2. The Avoider v3: 10 HP, จัดการความเสียหาย 0, รับความเสียหาย 0
  3. ต้องกินให้หมด: 10 HP, ทำดาเมจ 0 ค่า, ทำลายดาเมจเป็น 0
  4. BestOpportunityBot: 0 HP, 3 ดาเมจดาเมจ, ดาเมจ 10 ดาเมจ

แผนที่ 2

BestOpportunityBot ทำผลงานได้เกือบทั้งหมดในเกมนี้ แต่ Assassin ก็สามารถพาเขาออกมาได้ในที่สุด รายละเอียดการเล่นโดยการเล่นที่นี่

  1. นักฆ่า: 2 HP, 10 ความเสียหายจัดการ, 9 ความเสียหายที่ได้รับ
  2. BestOpportunityBot: 0 HP, 32 Dealt ความเสียหาย, 10 ดาเมจที่ได้รับ
  3. The Avoider v3: 0 HP, 0 การจัดการความเสียหาย, การทำดาเมจ 12 ครั้ง
  4. ต้องกินจนจบ: 0 HP, 0 ดาเมจทำดาเมจ, ดาเมจ 11 ดาเมจ

แผนที่ 3

BestOpportunityBot ผลักให้ทุกคนเข้าสู่กับดักในการแข่งขันครั้งนี้ เด็ดมาก รายละเอียดการเล่นโดยการเล่นที่นี่

  1. BestOpportunityBot: 10 HP, 30 Damage Dealt, 0 Damage ที่ได้รับ
  2. นักฆ่า: 0 HP, 0 ความเสียหายที่ทำ, ความเสียหายที่ได้รับ 0
  3. ต้องกินให้หมด: 0 HP, 0 ดาเมจทำดาเมจ, 0 ดาเมจที่ทำ
  4. The Avoider v3: 0 HP, 0 การทำดาเมจ 0, ดาเมจที่เกิดความเสียหาย

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


งานของคุณถ้าคุณเลือกที่จะรับมันก็คือการเขียนรหัส Scriptbot ซึ่งสามารถท่องแผนที่ ASCII และทำลายฝ่ายตรงข้าม การต่อสู้แต่ละครั้งจะอยู่ในรูปแบบของเกมเทิร์นเบสแบบสุ่มเริ่มต้นโดยที่ Scriptbot แต่ละคนมีโอกาสใช้คะแนนพลังงาน (EP) เพื่อดำเนินการ สคริปต์ GameMaster จะป้อนข้อมูลเข้าและตีความผลลัพธ์จาก Scriptbot แต่ละรายการ

สิ่งแวดล้อม

แต่ละ Scriptbot มีอยู่ในสารบบของตัวเองที่จะสามารถอ่านจากmapและstatsไฟล์และอ่าน / เขียนไปยังdataแฟ้ม dataไฟล์สามารถนำมาใช้ในการจัดเก็บข้อมูลแบบถาวรที่คุณอาจพบว่ามีประโยชน์

ไฟล์สถิติ

statsไฟล์มีข้อมูลเกี่ยวกับฝ่ายตรงข้ามของคุณและมีรูปแบบดังนี้ ผู้เล่นแต่ละคนจะแสดงในแถวที่แยกต่างหาก คอลัมน์แรกคือรหัสผู้เล่น ( @หมายถึงคุณ) คอลัมน์ที่สองคือสุขภาพของผู้เล่นนั้น

1,9HP
@,10HP
3,9HP
4,2HP

ไฟล์แผนที่

mapไฟล์อาจมีลักษณะบางอย่างเช่นนี้ ...

####################
#   #          #   #
# 1 #          # 2 #
#                  #
###              ###
#                  #
#      #           #
#       #     !    #
#        #         #
#       !####      #
#      ####!       #
#         #        #
#    !     #       #
#           #      #
#                  #
###              ###
#                  #
# 3 #          # @ #
#   #          #   #
####################

... หรือนี่ ...

######################################
#       # 1        #   @             #
#       #          #          #!     #
#       #          #          ####   #
#  #    #  #       #         !#!     #
#  #    #  #       #      #####      #
#  ###     #    ####    #         #  #
#          #    #!      ###       #  #
#  ######  #    #       #     #####  #
#  #!      #    ######  #        !#  #
#  ###     #            #         #  #
#  #    2  #            #   4     #  #
######################################

... หรือนี่ ...

###################
###!!!!!!#!!!!!!###
##!             !##
#! 1     !     2 !#
#!       !       !#
#!               !#
#!               !#
#!      !!!      !#
## !!   !!!   !! ##
#!      !!!      !#
#!               !#
#!               !#
#!       !       !#
#! 3     !     @ !#
##!             !##
###!!!!!!#!!!!!!###
###################

... หรือมันอาจดูแตกต่างอย่างสิ้นเชิง ไม่ว่าจะด้วยวิธีใดอักขระที่ใช้และความหมายจะยังคงเหมือนเดิม:

  • # ผนังที่ไม่สามารถใช้ได้และไม่ยอมรับ
  • 1, 2, 3... ตัวเลขที่แสดงเป็นผู้เล่นที่ศัตรู ตัวเลขเหล่านี้สอดคล้องกับรหัสประจำตัวผู้เล่นในstatsไฟล์
  • !กับดัก. Scriptbots ที่ย้ายไปยังสถานที่เหล่านี้จะตายทันที
  • @ ตำแหน่ง Scriptbot ของคุณ
  • พื้นที่เปิดโล่งซึ่งคุณสามารถเดินไปมาได้อย่างอิสระ

เพลย์

สคริปต์ GameMaster จะกำหนดลำดับเริ่มต้นแบบสุ่มให้กับ Scriptbots Scriptbots จะถูกเรียกใช้ในลำดับนี้ขณะที่ยังมีชีวิตอยู่ Scriptbots มี 10 คะแนนสุขภาพ (HP) และเริ่มต้นด้วย 10 คะแนนพลังงาน (EP) ทุกครั้งที่พวกเขาสามารถใช้เพื่อย้ายหรือโจมตี ในตอนเริ่มต้นของการเปิดแต่ละครั้ง Scriptbot จะรักษา HP หนึ่งตัวหรือจะได้รับ EP เพิ่มเติมหนึ่งอันหากมีอยู่ที่ 10 HP (ดังนั้นการวิ่งอาจเป็นกลยุทธ์ที่ใช้งานได้ในบางครั้ง)

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

  1. สุขภาพส่วนใหญ่
  2. ความเสียหายส่วนใหญ่กระทำ
  3. ความเสียหายส่วนใหญ่ที่ได้รับ

Scriptbot Input

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

:> example_scriptbot.py 3

Scriptbot จะถูกเรียกใช้จนกว่าจะใช้ EP ทั้งหมดหรือสูงสุด10 11 ครั้ง ไฟล์แผนที่และสถิติจะได้รับการอัพเดตก่อนการร้องขอแต่ละครั้ง

Scriptbot เอาท์พุต

Scriptbots ควรแสดงผลการกระทำของพวกเขาเพื่ออ้วน รายการของการกระทำที่เป็นไปได้มีดังนี้:

  • MOVE <DIRECTION> <DISTANCE>

    ค่าใช้จ่าย 1 EP DISTANCEต่อ MOVEคำสั่งย้าย Scriptbot ไปรอบ ๆ แผนที่ หากมีบางอย่างในทางเช่นกำแพงหรือ Scriptbot อื่น GameMaster จะย้าย Scriptbot ของคุณเท่าที่จะทำได้ หากมีDISTANCEมากกว่า EP ที่เหลือของ Scriptbot GameMaster จะย้าย Scriptbot จนกว่า EP ของมันจะหมด DIRECTIONอาจจะเป็นทิศทางใด ๆ ของเข็มทิศN, E, หรือSW

  • PUSH <DIRECTION> <DISTANCE>

    ค่าใช้จ่าย 1 EP DISTANCEต่อ PUSHคำสั่งช่วยให้ Scriptbot ที่จะย้าย Scriptbot อื่น Scriptbot ที่ออกคำสั่งจะต้องอยู่ถัดจาก Scriptbot ที่ถูกผลักโดยตรง Scriptbots ทั้งสองจะเคลื่อนที่ไปในทิศทางที่ระบุหากไม่มีวัตถุที่ถูกบล็อค Scriptbot ที่ถูกผลัก DIRECTIONและDISTANCEเป็นเช่นเดียวกับMOVEคำสั่ง

  • ATTACK <DIRECTION>

    เสียค่าใช้จ่ายหนึ่ง EP ATTACKคำสั่งข้อตกลง 1 ความเสียหายให้กับ Scriptbot โดยตรงต่อไปที่จะออก Scriptbot และในทิศทางที่ระบุไว้ DIRECTIONเป็นเช่นเดียวกับMOVEคำสั่ง

  • PASS

    จบเทิร์นของคุณ

ภาษาที่รองรับ

เพื่อให้เหมาะสมกับฉันฉันจะยอมรับภาษาต่อไปนี้:

  • ชวา
  • Node.js
  • หลาม
  • PHP

คุณถูก จำกัด ไว้ที่ห้องสมุดซึ่งบรรจุด้วยภาษาของคุณโดยทั่วไป โปรดอย่าทำให้ฉันค้นหาไลบรารีที่ไม่ชัดเจนเพื่อให้โค้ดของคุณใช้งานได้

การส่งและการตัดสิน

โพสต์ซอร์สโค้ด Scriptbot ของคุณด้านล่างและตั้งชื่อที่ดี! โปรดระบุรุ่นภาษาที่คุณใช้ด้วย Scriptbots ทั้งหมดจะถูกตรวจสอบสำหรับ tomfoolery ดังนั้นโปรดแสดงความคิดเห็นให้ดีและอย่าทำให้รหัสของคุณยุ่งเหยิง

คุณอาจส่งมากกว่าหนึ่งรายการ แต่โปรดทำให้เป็นรายการที่ไม่ซ้ำกันโดยสิ้นเชิงและไม่ใช่รุ่นเดียวกัน ตัวอย่างเช่นคุณอาจต้องการรหัสบอท Zerg Rush และบอร์กอริลลาสงคราม ไม่เป็นไร. อย่าโพสต์ Zerg Rush v1, Zerg Rush v2 เป็นต้น

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

BOT01_
BOT02_|
BOT03_|____
BOT04_|    |
           |
BOT05_     |
BOT06_|___ |
BOT07_|  | |
BOT08_|  | |_BOT ?_
         |___BOT ?_|
BOT09_    ___BOT ?_|___CHAMPION!
BOT10_|  |  _BOT ?_|
BOT11_|__| |
BOT12_|    |
           |
BOT13_     |
BOT14_|____|
BOT15_|
BOT16_|

Q & A

ฉันแน่ใจว่าฉันไม่ได้รับรายละเอียดดังนั้นอย่าลังเลที่จะถามคำถาม!

ขอให้เราเชื่อมั่นว่าไฟล์แผนที่ล้อมรอบด้วยสัญลักษณ์ # เสมอหรือไม่ ถ้าไม่เป็นเช่นนั้นจะเกิดอะไรขึ้นในกรณีที่บอทพยายามเดินออกจากแผนที่ - BrainSteel

ใช่แผนที่จะถูกล้อมรอบด้วย # และ Scriptbot ของคุณจะเริ่มต้นภายในขอบเขตเหล่านี้

หากไม่มีบอทอยู่ในทิศทางที่ระบุในคำสั่ง PUSH ฟังก์ชันคำสั่งจะทำงานอย่างไร - BrainSteel

GameMaster จะไม่ทำอะไรเลยจะใช้ศูนย์ EP และ Scriptbot จะถูกเรียกอีกครั้ง

EP ที่ไม่ได้ใช้มีการสะสมหรือไม่ - feersum

ไม่แต่ละ Scriptbot จะเริ่มรอบ / หมุนด้วย 10 EP EP ใด ๆ ที่ไม่ได้ใช้จะเสียไป

ฉันคิดว่าฉันเข้าใจแล้ว แต่เพื่อชี้แจง: ด้วยบอต A และ B คือลำดับของเหตุการณ์ A @ 10EP-> MOVE MAP_UPDATE B @ 10EP-> PUSH MAP_UPDATE A @ 9EP-> ATTACK MAP_UPDATE B @ 9EP-> การโจมตี ... หรือ A @ 10EP-> ย้าย A @ 9EP-> ATTACK ... MAP_UPDATE B @ 10EP-> PUSH B @ 9EP-> ATTACK ... MAP_UPDATE หรือไม่ ในคำอื่น ๆ การกระทำทั้งหมดในอะตอมลูปคิวรีคอนโทรลเลอร์บอทควบคุมเดียวหรือไม่ ถ้าเป็นเช่นนั้นทำไมลูป? ทำไมไม่ส่งคืนไฟล์เดียวพร้อมกับการกระทำทั้งหมดที่จะทำให้เสร็จ มิฉะนั้นบ็อตจะต้องเขียนไฟล์สถานะของตัวเองเพื่อติดตามลำดับการทำงานหลายแบบ ไฟล์ map / stats จะใช้ได้ก่อนดำเนินการครั้งแรกเท่านั้น - COTO

ตัวอย่างที่สองของคุณใกล้เคียง แต่ไม่ถูกต้องนัก ในระหว่างการหมุน Scriptbot จะถูกเรียกซ้ำ ๆ จนกระทั่ง EP ถูกใช้ไปหรือสูงสุด 11 ครั้ง ไฟล์แผนที่และสถิติจะได้รับการอัพเดตก่อนการร้องขอแต่ละครั้ง การวนซ้ำนั้นมีประโยชน์ในกรณีที่ bot ให้ผลลัพธ์ที่ไม่ถูกต้อง GameMaster จะจัดการกับเอาต์พุตที่ไม่ถูกต้องและทำให้บ็อตกลับมาอีกครั้งทำให้บอทมีโอกาสที่จะแก้ไขเพราะมันผิดพลาด

คุณจะปล่อยสคริปต์ GameMaster เพื่อทำการทดสอบหรือไม่ - IchBinKeinBaum

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

หาก robotA ผลักดันหุ่นยนต์ B ไปยังกับดักหุ่นยนต์จะให้เครดิตกับ "ดาเมจที่ได้รับความเสียหาย" ซึ่งเท่ากับคะแนนปัจจุบันของหุ่นยนต์ B หรือไม่ - Mike Sweeney

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


ขอให้เราเชื่อว่าmapไฟล์นั้นล้อมรอบด้วย#สัญลักษณ์เสมอ? ถ้าไม่เป็นเช่นนั้นจะเกิดอะไรขึ้นในกรณีที่บอทพยายามเดินออกจากแผนที่
BrainSteel

@BrainSteel ใช่แผนที่จะถูก จำกัด ด้วยเสมอ#และ Scriptbot ของคุณจะเริ่มต้นภายในขอบเขตเหล่านี้
Rip Leeb

3
หากคุณแน่ใจว่าพลาดอะไรไปทำไมไม่โพสต์ไว้ในแซนด์บ็อกซ์ล่ะ?
Martin Ender

2
@ MartinBüttnerฉันคิดเรื่องนี้ค่อนข้างละเอียด ฉันแค่พยายามเป็นมิตรและทำให้ชัดเจนว่ายินดีต้อนรับคำถาม
Rip Leeb

1
หาก robotA ผลักดันหุ่นยนต์ B ไปยังกับดักหุ่นยนต์จะให้เครดิตกับ "ดาเมจที่ได้รับความเสียหาย" ซึ่งเท่ากับคะแนนปัจจุบันของหุ่นยนต์ B หรือไม่
Logic Knight

คำตอบ:


1

Assassin (Java 1.7)

พยายามฆ่าศัตรูทุกครั้งที่ทำได้ มันค่อนข้างดีในการค้นหาเส้นทางไปยังศัตรู แต่ไม่ได้ทำอะไรเลยเพื่อซ่อนจากบอทอื่น ๆ

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

public class Assassin {
    private final Path dataPath = Paths.get("data");
    private final Path mapPath = Paths.get("map");
    private final Path statsPath = Paths.get("stats");
    private final List<Player> players = new ArrayList<>();
    private final int energy;
    private Map map = null;

    public Assassin(int energy) {
        this.energy = energy;
    }

    private void doSomething() {
        if (dataFileEmpty()) {
            calculateTurn();
        }
        printStoredOutput();
    }

    private boolean dataFileEmpty() {
        try {
            return !Files.exists(dataPath) || Files.size(dataPath)  == 0;
        } catch (IOException e) {
            return true;
        }
    }

    private void printStoredOutput() {
        try {
            List<String> lines = Files.readAllLines(dataPath, StandardCharsets.UTF_8);
            //print first line
            System.out.println(lines.get(0));           
            //delete first line
            lines.remove(0);
            Files.write(dataPath, lines, StandardCharsets.UTF_8);
        } catch (IOException e) {
            System.out.println("PASS");
        }
    }

    private void calculateTurn() {
        try {
            readStats();
            readMap();
            if (!tryKill())  {
                sneakCloser();
            }
        } catch (IOException e) {}
    }

    private void readStats() throws IOException{
        List<String> stats = Files.readAllLines(statsPath, StandardCharsets.UTF_8);
        for (String stat : stats) {
            String[] infos = stat.split(",");
            int hp = Integer.parseInt(infos[1].replace("HP", ""));
            players.add(new Player(stat.charAt(0), hp));
        }
    }

    private void readMap() throws IOException{
        List<String> lines = Files.readAllLines(mapPath, StandardCharsets.UTF_8);
        Field[][] fields = new Field[lines.size()][lines.get(0).length()];
        for (int row = 0; row < lines.size(); row++) {
            String line = lines.get(row);
            for (int col = 0; col < line.length(); col++) {
                fields[row][col] = new Field(line.charAt(col), row, col);
            }
        }
        map = new Map(fields);
    }

    private boolean tryKill() {     
        Field me = map.getMyField();
        for (Field field : map.getEnemyFields()) {
            for (Field surrField : map.surroundingFields(field)) { 
                List<Direction> dirs = map.path(me, surrField);
                if (dirs != null) {
                    for (Player player : players) {
                        //can kill this player
                        int remainderEnergy = energy - dirs.size() - player.hp;
                        if (player.id == field.content && remainderEnergy >= 0) {
                            //save future moves
                            List<String> commands = new ArrayList<>();
                            for (Direction dir : dirs) {
                                commands.add("MOVE " + dir + " 1");
                            }
                            //attacking direction
                            Direction attDir = surrField.dirsTo(field).get(0);
                            for (int i = 0; i < player.hp; i++) {
                                commands.add("ATTACK " + attDir);                               
                            }
                            if (remainderEnergy > 0) {
                                commands.add("PASS");
                            }
                            try {
                                Files.write(dataPath, commands, StandardCharsets.UTF_8);
                            } catch (IOException e) {}
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }

    private void sneakCloser() {        
        Field me = map.getMyField();
        for (Direction dir : Direction.values()) {
            if (!map.move(me, dir).blocked()) {
                List<String> commands = new ArrayList<>();
                commands.add("MOVE " + dir + " 1");
                commands.add("PASS");
                try {
                    Files.write(dataPath, commands, StandardCharsets.UTF_8);
                } catch (IOException e) {}
                return;
            }
        }
    }

    public static void main(String[] args) {
        try {
            new Assassin(Integer.parseInt(args[0])).doSomething();
        } catch (Exception e) {
            System.out.println("PASS");
        }
    }

    class Map {
        private Field[][] fields;

        public Map(Field[][] fields) {
            this.fields = fields;
        }

        public Field getMyField() {
            for (Field[] rows : fields) {
                for (Field field : rows) {
                    if (field.isMyPos()) {
                        return field;
                    }
                }
            }
            return null; //should never happen
        }   

        public List<Field> getEnemyFields() {
            List<Field> enemyFields = new ArrayList<>();
            for (Field[] rows : fields) {
                for (Field field : rows) {
                    if (field.hasEnemy()) {
                        enemyFields.add(field);
                    }
                }
            }
            return enemyFields;
        }

        public List<Field> surroundingFields(Field field) {
            List<Field> surrFields = new ArrayList<>();
            for (Direction dir : Direction.values()) {
                surrFields.add(move(field, dir));
            }
            return surrFields;
        }

        public Field move(Field field, Direction dir) {
            return fields[field.row + dir.rowOffset][field.col + dir.colOffset];
        }

        public List<Direction> path(Field from, Field to) {
            List<Direction> dirs = new ArrayList<>();
            Field lastField = from;
            boolean changed = false;

            for (int i = 0; i < energy && lastField != to; i++) {
                List<Direction> possibleDirs = lastField.dirsTo(to);
                changed = false;
                for (Direction dir : possibleDirs) {
                    Field nextField = move(lastField, dir);
                    if (!nextField.blocked()) {
                        lastField = nextField;
                        changed = true;
                        dirs.add(dir);
                        break;
                    }
                }
                if (!changed) {
                    return null; //not possible
                }
            }
            if (lastField != to) {
                return null; //not enough energy
            }           
            return dirs;
        }
    }

    class Field {
        private char content;
        private int row;
        private int col;

        public Field(char content, int row, int col) {
            this.content = content;
            this.row = row;
            this.col = col;
        }

        public boolean hasEnemy() {
            return content == '1' || content == '2' || content == '3' || content == '4';
        }

        public List<Direction> dirsTo(Field field) {
            List<Direction> dirs = new ArrayList<>();
            int distance = Math.abs(row - field.row) + Math.abs(col - field.col);

            for (Direction dir : Direction.values()) {
                int dirDistance = Math.abs(row - field.row + dir.rowOffset) + 
                        Math.abs(col - field.col + dir.colOffset);
                if (dirDistance < distance) {
                    dirs.add(dir);
                }
            }
            if (dirs.size() == 1) { //add near directions
                for (Direction dir : dirs.get(0).nearDirections()) {
                    dirs.add(dir);
                }
            }
            return dirs;
        }

        public boolean isMyPos() {
            return content == '@';
        }

        public boolean blocked() {
            return content != ' ';
        }
    }

    class Player {
        private char id;
        private int hp;

        public Player(char id, int hp) {
            this.id = id;
            this.hp = hp;
        }
    }

    enum Direction {
        N(-1, 0),S(1, 0),E(0, 1),W(0, -1);

        private final int rowOffset;
        private final int colOffset;

        Direction(int rowOffset, int colOffset) {
            this.rowOffset = rowOffset;
            this.colOffset = colOffset;
        }

        public Direction[] nearDirections() {
            Direction[] dirs = new Direction[2];
            for (int i = 0; i < Direction.values().length; i++) {
                Direction currentDir = Direction.values()[i];
                if (Math.abs(currentDir.rowOffset) != Math.abs(this.rowOffset)) {
                    dirs[i%2] = currentDir;
                }
            }
            return dirs;
        }
    }
}

Java เวอร์ชันนี้เขียนด้วยภาษาอะไร?
Rip Leeb

@Nate ฉันใช้ 1.7
CommonGuy

3

The Avoider v3

บอทใจที่เรียบง่าย มันกลัวหุ่นยนต์และกับดักอื่น ๆ มันจะไม่โจมตี มันละเว้นไฟล์สถิติและไม่คิดล่วงหน้าเลย

นี่คือการทดสอบเป็นหลักเพื่อดูว่ากฎจะทำงานอย่างไรและเป็นคู่แข่งที่โง่สำหรับคู่แข่งอื่น ๆ

แก้ไข: ตอนนี้จะผ่านเมื่อไม่มีการเคลื่อนย้ายที่ดีขึ้น

แก้ไข 2: หุ่นยนต์สามารถ '1234' แทน '123'

รหัสหลาม:

import sys
maxmoves = int(sys.argv[1])

ORTH = [(-1,0), (1,0), (0,-1), (0,1)]
def orth(p):
    return [(p[0]+dx, p[1]+dy) for dx,dy in ORTH]

mapfile = open('map').readlines()[::-1]
arena = dict( ((x,y),ch) 
    for y,row in enumerate(mapfile)
    for x,ch in enumerate(row.strip()) )
loc = [loc for loc,ch in arena.items() if ch == '@'][0]

options = []
for direc in ORTH:
    for dist in range(maxmoves+1):
        newloc = (loc[0]+direc[0]*dist, loc[1]+direc[1]*dist)
        if arena.get(newloc) in '#!1234':
            break
        penalty = dist * 10  # try not to use moves too fast
        if newloc == loc:
            penalty += 32   # incentive to move
        for nextto in orth(newloc):
            ch = arena.get(nextto)
            if ch == '#':
                penalty += 17  # don't get caught againt a wall
            elif ch in '1234':
                penalty += 26  # we are afraid of other robots
            elif ch == '!':
                penalty += 38  # we are very afraid of deadly traps
        options.append( [penalty, dist, direc] )

penalty, dist, direc = min(options)
if dist > 0:
    print 'MOVE', 'WESN'[ORTH.index(direc)], dist
else:
    print 'PASS'  # stay still?

elif ch in '123':คุณต้องการค้นหาอย่างน้อย 1234 ถ้าคุณเป็นบอท 3 รายชื่อคู่ต่อสู้จะเป็น 124
Rip Leeb

1
@ เนทขอบคุณ ฉันเปลี่ยนบอท คุณอาจต้องการทำให้ชัดเจนในกฎ ฉันอาจไม่ใช่คนเดียวที่เข้าใจผิด
Logic Knight

3

BestOpportunityBot

จบลงด้วยการใช้เวลานานกว่าที่ฉันตั้งใจ ... และฉันไม่แน่ใจว่าฉันเข้าใจกฎของการเลี้ยวทั้งหมดหรือไม่ดังนั้นเราจะเห็นว่าสิ่งนี้ทำได้อย่างไร

from sys import argv
from enum import IntEnum

with open("map") as map_file:
    map_lines = map_file.read().splitlines()

with open("stats") as stats_file:
    stats_data = stats_file.read().splitlines()

ep = argv[1]

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __add__(lhs, rhs):
        if (type(rhs) == tuple or type(rhs) == list):
            return Point(lhs.x + rhs[0], lhs.y + rhs[1])
        return Point(lhs.x + rhs.x, lhs.y + rhs.y)

    def __sub__(lhs, rhs):
        if (type(rhs) == tuple or type(rhs) == list):
            return Point(lhs.x - rhs[0], lhs.y - rhs[1])
        return Point(lhs.x - rhs.x, lhs.y - rhs.y)

    def __mul__(lhs, rhs):
        return Point(lhs.x * rhs, lhs.y * rhs)

    def __str__(self):
        return "(" + str(self.x) + ", " + str(self.y) + ")"

    def __repr__(self):
        return "(" + str(self.x) + ", " + str(self.y) + ")"

    def __eq__(lhs, rhs):
        return lhs.x == rhs.x and lhs.y == rhs.y

    def __hash__(self):
        return hash(self.x) * 2**32 + hash(self.y)

    def reverse(self):
        return Point(self.y, self.x)

dirs = (Point(0, 1), Point(1, 0), Point(0, -1), Point(-1, 0))

class Bot:
    def __init__(self, pos, ch, hp):
        self.pos = pos
        self.ch = ch
        self.hp = hp

    def __str__(self):
        return str(self.pos) + " " + str(self.ch) + " " + str(self.hp)

    def __repr__(self):
        return str(self.pos) + " " + str(self.ch) + " " + str(self.hp)

class MyBot(Bot):
    def __init__(self, pos, ch, hp, ep):
        self.ep = ep
        super().__init__(pos, ch, hp)

    def __str__(self):
        return super().__str__() + " " + self.ep

    def __repr__(self):
        return super().__repr__() + " " + self.ep

class Arena:
    def __init__(self, orig_map):
        self._map = list(zip(*orig_map[::-1]))
        self.bots = []
        self.me = None

    def __getitem__(self, indexes):
        if (type(indexes) == Point):
            return self._map[indexes.x][indexes.y]
        return self._map[indexes[0]][indexes[1]]

    def __str__(self):
        output = ""
        for i in range(len(self._map[0]) - 1, -1, -1):
            for j in range(len(self._map)):
                output += self._map[j][i]
            output += "\n"
        output = output[:-1]
        return output

    def set_bot_loc(self, bot):
        for x in range(len(self._map)):
            for y in range(len(self._map[x])):
                if self._map[x][y] == bot.ch:
                    bot.pos = Point(x, y)

    def set_bots_locs(self):
        self.set_bot_loc(self.me)
        for bot in self.bots:
            self.set_bot_loc(bot)

    def adjacent_bots(self, pos=None):
        if type(pos) == None:
            pos = self.me.pos
        output = []
        for bot in self.bots:
            for d in dirs:
                if bot.pos == pos + d:
                    output.append(bot)
                    break
        return output

    def adjacent_bots_and_dirs(self):
        output = {}
        for bot in self.bots:
            for d in dirs:
                if bot.pos == self.me.pos + d:
                    yield bot, d
                    break
        return output

    def look(self, position, direction, distance, ignore='', stopchars='#1234'):
        current = position + direction
        output = []
        for i in range(distance):
            if (0 <= current.x < len(self._map) and
                0 <= current.y < len(self._map[current.x]) and
                (self[current] not in stopchars or self[current] in ignore)):
                output += self[current]
                current = current + direction
            else:
                break
        return output

    def moveable(self, position, direction, distance):
        current = position + direction
        output = []
        for i in range(distance):
            if (0 <= current.x < len(self._map) and
                0 <= current.y < len(self._map[current.x]) and
                self[current] == ' '):
                output += self[current]
            else:
                break
        return output


    def danger(self, pos, ignore=None): # damage that can be inflicted on me
        output = 0
        adjacents = self.adjacent_bots(pos)
        hps = [bot.hp for bot in adjacents if bot != ignore]
        if len(hps) == 0:
            return output

        while max(hps) > 0:
            if 0 in hps:
                hps.remove(0)

            for i in range(len(hps)):
                if hps[i] == min(hps):
                    hps[i] -= 1
                output += 1
        return output

    def path(self, pos): # Dijkstra's algorithm adapted from https://gist.github.com/econchick/4666413
        visited = {pos: 0}
        path = {}

        nodes = set()

        for i in range(len(self._map)):
            for j in range(len(self._map[0])):
                nodes.add(Point(i, j))

        while nodes:
            min_node = None
            for node in nodes:
                if node in visited:
                    if min_node is None:
                        min_node = node
                    elif visited[node] < visited[min_node]:
                        min_node = node

            if min_node is None:
                break

            nodes.remove(min_node)
            current_weight = visited[min_node]

            for _dir in dirs:
                new_node = min_node + _dir
                if self[new_node] in ' 1234':
                    weight = current_weight + 1
                    if new_node not in visited or weight < visited[new_node]:
                        visited[new_node] = weight
                        path[new_node] = min_node

        return visited, path

class MoveEnum(IntEnum):
    Null = 0
    Pass = 1
    Attack = 2
    Move = 3
    Push = 4

class Move:
    def __init__(self, move=MoveEnum.Null, direction=Point(0, 0), distance=0):
        self.move = move
        self.dir = direction
        self.dis = distance

    def __repr__(self):
        if self.move == MoveEnum.Null:
            return "NULL"
        elif self.move == MoveEnum.Pass:
            return "PASS"
        elif self.move == MoveEnum.Attack:
            return "ATTACK " + "NESW"[dirs.index(self.dir)]
        elif self.move == MoveEnum.Move:
            return "MOVE " + "NESW"[dirs.index(self.dir)] + " " + str(self.dis)
        elif self.move == MoveEnum.Push:
            return "PUSH " + "NESW"[dirs.index(self.dir)] + " " + str(self.dis)

arena = Arena(map_lines)
arena.me = MyBot(Point(0, 0), '@', 0, ep)

for line in stats_data:
    if line[0] == '@':
        arena.me.hp = int(line[2:-2])
    else:
        arena.bots.append(Bot(Point(0, 0), line[0], int(line[2:-2])))

arena.set_bots_locs()

current_danger = arena.danger(arena.me.pos)

moves = [] # format (move, damage done, danger, energy)

if arena.me.ep == 0:
    print(Move(MoveEnum.Pass))
    exit()

for bot, direction in arena.adjacent_bots_and_dirs():
    # Push to damage
    pushable_to = arena.look(arena.me.pos, direction,
                             arena.me.ep + 1, ignore=bot.ch)
    if '!' in pushable_to:
        distance = pushable_to.index('!')
        danger = arena.danger(arena.me.pos + (direction * distance), bot)
        danger -= current_danger
        moves.append((Move(MoveEnum.Push, direction, distance),
                      bot.hp, danger, distance))

    # Push to escape
    pushable_to = arena.look(arena.me.pos, direction,
                             arena.me.ep + 1, ignore=bot.ch, stopchars='#1234!')
    for distance in range(1, len(pushable_to)):
        danger = arena.danger(arena.me.pos + (direction * distance), bot)
        danger += bot.hp
        danger -= current_danger
        moves.append((Move(MoveEnum.Push, direction, distance),
                      0, danger, distance))

    # Attack
    bot.hp -= 1
    danger = arena.danger(arena.me.pos) - current_danger
    moves.append((Move(MoveEnum.Attack, direction), 1, danger, 1))
    bot.hp += 1

culled_moves = []

for move in moves: # Cull out attacks and pushes that result in certain death
    if current_danger + move[2] < arena.me.hp:
        culled_moves.append(move)

if len(culled_moves) == 1:
    print(culled_moves[0][0])
    exit()
elif len(culled_moves) > 1:
    best_move = culled_moves[0]

    for move in culled_moves:
        if move[1] - move[2] > best_move[1] - best_move[2]:
            best_move = move
        if move[1] - move[2] == best_move[1] - best_move[2] and move[3] < best_move[3]:
            best_move = move

    print (best_move[0])
    exit()

# Can't attack or push without dying, time to move

moves = []

if current_danger > 0: # Try to escape
    for direction in dirs:
        moveable_to = arena.moveable(arena.me.pos, direction, ep)

        i = 1

        for space in moveable_to:
            danger = arena.danger(arena.me.pos + (direction * i))
            danger -= current_danger
            moves.append((Move(MoveEnum.Move, direction, i), 0, danger, i))
            i += 1

    if len(moves) == 0: # Trapped and in mortal danger, attack biggest guy
        adjacents = arena.adjacent_bots()
        biggest = adjacents[0]
        for bot in adjacents:
            if biggest.hp < bot.hp:
                biggest = bot
        print (Move(MoveEnum.Attack, biggest.pos - arena.me.pos))
        exit()

    best_move = moves[0]
    for move in moves:
        if ((move[2] < best_move[2] and best_move[2] >= arena.me.hp) or
            (move[2] == best_move[2] and move[3] < best_move[3])):
            best_move = move

    print(best_move[0])
    exit()

else: # Seek out closest target with lower health
    distances, path = arena.path(arena.me.pos)

    bot_dists = list((bot, distances[bot.pos]) for bot in arena.bots)

    bot_dists.sort(key=lambda x: x[1])

    target = bot_dists[0]

    for i in range(len(bot_dists)):
        if bot_dists[i][0].hp <= arena.me.hp:
            target = bot_dists[i]
            break

    pos = target[0].pos
    for i in range(target[1] - 1):
        pos = path[pos]

    print (Move(MoveEnum.Move, pos - arena.me.pos, 1))
    exit()

# Shouldn't get here, but I might as well do something
print (Move(MoveEnum.Pass))

หลามรุ่นไหนที่คุณเขียนมัน?
Rip Leeb

@Nate 3.4.1 ใน win32

ขอบคุณที่ส่ง @horns ตัวนี้ มันสนุกมากที่ได้ดู!
Rip Leeb

1

ต้องกินเสร็จสิ้น

งูหลาม:

import sys
print 'PASS'

1
ฉัน lol'd ที่ว่า - และทำไมนรกimport sys?
tomsmeding

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