ความเสี่ยงทาง Warlight


12

บทนำ

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

บอตทั้งหมดควรเขียนใน Java, C หรือ C ++ (ฉันจะรวมภาษาอื่น แต่ไม่มีซอฟต์แวร์หรือประสบการณ์สำหรับพวกเขา) มันไม่ได้เป็นสิ่งที่จำเป็นสำหรับการส่งของคุณจะขยายชั้นเรียนและคุณอาจสร้างฟังก์ชั่น, เรียน, อินเตอร์เฟซหรือสิ่งอื่นเป็นสิ่งที่จำเป็นและใช้แพคเกจหรือระดับใด ๆ ในมาตรฐาน APIs หากคุณวางแผนที่จะสร้างคลาสหรืออินเทอร์เฟซโปรดพิจารณาใช้คลาสภายในหรืออินเทอร์เฟซภายใน

โปรดอย่าพยายามแก้ไขตัวควบคุมหรือการส่งอื่น ๆ โดยทางโปรแกรมในการแข่งขันครั้งนี้

เพลย์

ภาพรวม

อาร์เรย์สองมิติขนาด 10x10 จะจำลองบอร์ดซึ่งแต่ละองค์ประกอบ / เซลล์แสดงถึง "อาณาเขต" จะมี 20 รอบและสูงถึง 1,000 รอบต่อรอบ ผู้เล่นแต่ละคนจะปรับกองทัพที่พวกเขามีให้กับดินแดนใด ๆ ที่พวกเขาเป็นเจ้าของและจากนั้นจะได้รับโอกาสในการส่งกองทัพของพวกเขาไปยังดินแดนใกล้เคียงในความพยายามที่จะยึดครองดินแดนของฝ่ายตรงข้าม ผู้เล่นจะต้องปรับใช้กองทัพทั้งหมดของพวกเขา แต่พวกเขาไม่จำเป็นต้องย้ายพวกเขาหากต้องการ

โจมตี / ถ่ายโอนกองทัพ

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

หากผู้เล่นส่งnกองทัพจากดินแดนหนึ่งไปยังอีกดินแดนที่พวกเขาเป็นเจ้าของดินแดนนั้นจะได้รับnกองทัพ

สมมติว่าผู้เล่นส่งnกองทัพจากดินแดนของเขา / เธอไปยังดินแดนฝ่ายตรงข้ามโดยมีoกองทัพอยู่ในนั้น oจะลดลงโดยn * .6ปัดเศษเป็นจำนวนเต็มที่ใกล้ที่สุด อย่างไรก็ตามในเวลาเดียวกันnจะลดลงโดยo * .7ปัดเป็นจำนวนเต็มที่ใกล้ที่สุด กฎต่อไปนี้เกี่ยวข้องกับการยึดครองดินแดนฝ่ายตรงข้ามหรือไม่:

  • หากoถึงศูนย์และnมากกว่า 0 ผู้เล่นจะเข้ายึดครองดินแดนซึ่งจะมีnกองทัพอยู่ในนั้น
  • หากทั้งสองnและoกลายเป็นศูนย์oจะถูกตั้งค่าเป็น 1 โดยอัตโนมัติและอาณาเขตจะไม่ถูกยึด
  • หากoยังคงมีค่ามากกว่า 0 จำนวนกองทัพในดินแดนของผู้เล่นจะเพิ่มขึ้นnและดินแดนฝ่ายตรงข้ามจะไม่ถูกยึด

โบนัส

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

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

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

Input / Output

โปรแกรมของคุณควรรับอินพุตผ่านอาร์กิวเมนต์บรรทัดคำสั่งซึ่งจะมีรูปแบบต่อไปนี้:

[id] [armies] [territories (yours and all adjacent ones)] [bonuses] ["X" (if first turn)]
  • idและarmiesเป็นตัวเลขทั้งจำนวน idคือรหัสของคุณและarmiesเป็นจำนวนกองทัพที่คุณต้องนำไปใช้กับดินแดนของคุณ คุณต้องปรับใช้กองทัพทั้งหมดที่มอบให้คุณ - ไม่มากและไม่น้อย
  • territoriesเป็นชุดของสตริงที่แสดงถึงอาณาเขตที่คุณเป็นเจ้าของและอาณาเขตที่คุณไม่ได้เป็นเจ้าของที่อยู่ติดกับคุณ สตริงอยู่ในรูปแบบนี้:

    [row],[col],[bonus id],[player id],[armies]
    

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

  • bonusesเป็นชุดของสตริงที่แสดงถึงโบนัสบนกระดานที่คุณสามารถใช้ประโยชน์ได้ สตริงอยู่ในรูปแบบนี้:

    [id],[armies],[territories left]
    

    idคือรหัสของโบนัสarmiesคือจำนวนกองทัพพิเศษที่คุณสามารถรับได้โดยการเป็นเจ้าของพื้นที่ทั้งหมดในโบนัสนี้และterritories leftเป็นจำนวนพื้นที่ในโบนัสที่คุณต้องจับเพื่อรับกองทัพพิเศษ

โปรดทราบว่าอาร์กิวเมนต์ที่ห้าคือ "X" จะปรากฏขึ้นหากเป็นการหมุนรอบแรกและสามารถใช้เพื่อความสะดวก

ตัวอย่างของการป้อนข้อมูลในเทิร์นแรก:

0 5 "7,6,7,-1,2 8,7,7,-1,2 7,7,7,0,5 6,6,7,-1,2 8,8,9,-1,2 6,7,7,-1,2 7,8,9,-1,2 6,8,9,-1,2 8,6,7,-1,2" "0,5,10 1,5,10 2,9,10 3,9,10 4,9,10 5,5,10 6,5,10 7,6,9 8,7,10 9,7,10" X

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

หากต้องการระบุอาณาเขตที่คุณต้องการเพิ่มกองทัพเอาท์พุทของคุณควรเป็นไปตามรูปแบบนี้:

[row],[col],[armies]

rowและcolเป็นแถวและคอลัมน์ของกระดานที่อาณาเขตที่คุณต้องการเพิ่มกองทัพเป็นและarmiesเป็นจำนวนกองทัพที่คุณต้องการเพิ่มลงในดินแดน

หากต้องการระบุว่าคุณต้องการส่งกองทัพไปพื้นที่ใดเอาท์พุทของคุณควรเป็นไปตามรูปแบบนี้:

[srow],[scol],[drow],[dcol],[armies]

srowและscolเป็นแถวและคอลัมน์ของกระดานที่อาณาเขตที่คุณต้องการส่งกองทัพคือdrowและdcolเป็นแถวและคอลัมน์ของกระดานที่อาณาเขตที่คุณต้องการส่งกองทัพไปคือและarmiesจำนวนกองทัพที่คุณต้องการส่ง . โปรดทราบว่าหากคุณไม่ต้องการย้ายกองทัพใด ๆ โปรแกรมของคุณควรพิมพ์เว้นวรรค

ผลลัพธ์ตัวอย่างอาจเป็นดังนี้:

0,0,5
0,0,0,1,3 0,0,1,0,3 0,0,1,1,3

ในกรณีนี้ผู้เล่นปรับใช้ห้ากองทัพไปยังดินแดนที่ 0,0 และย้ายสามกองทัพจาก 0,0 ถึง 0,1; สามจาก 0,0 ถึง 1,0; และสามจาก 0,0 ถึง 1,1

รอบและรอบ

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

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

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

การให้คะแนนและการชนะ

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

การส่ง

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

อื่น ๆ

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

ตาคุณจะถูกข้ามถ้า:

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

คำสั่งโจมตี / ถ่ายโอนของคุณจะไม่ถูกดำเนินการหาก:

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

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

ผล

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

As of 25-08-15, 04:40 UTC

1: WeSwarm           1420
2: java Player        120
   java LandGrab      120
   java Hermit        120
   java Castler       120
6: java RandomHalver   80

แจ้งให้ทราบ!

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


JavaScript? มันสามารถทำงานได้ในคอนโซลของเบราว์เซอร์ใด ๆ
Downgoat

ขออภัย แต่ไม่ใช่; ฉันต้องการให้การส่งเป็นหนึ่งในสามภาษาข้างต้น
ทีเอ็นที

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

@Mogogie สิ่งนี้เกิดขึ้นกับแต่ละ bot หรือไม่ มันออกมาอย่างสม่ำเสมอ (แต่ละเทิร์น) หรือเป็นระยะ (ทุกสองรอบ)? และคุณใช้ "java Player" ในอาเรย์สามครั้งหรือคุณสร้างคลาสแยกกันหรือไม่?
ทีเอ็นที

@TNT ตกลงมันเป็นปัญหาของฉัน ... เราจริง ๆ แล้วปัญหาของ IDE: P เปลี่ยนคำสั่งเป็น "java -cp bin Player" และมันก็ดีทั้งหมด ขอโทษสำหรับสิ่งนั้น.
Moogie

คำตอบ:


6

Castler - Java 8

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

[

คลิกที่ภาพเพื่อดูภาพเคลื่อนไหว gif (15 เมกะไบต์) จากการจำลองการเลี้ยว 20x1000 เต็ม Castler ได้คะแนน 1,700 และผู้เล่นคนอื่น ๆ ได้คะแนน 100 ต่อคน

import java.util.*;
import java.util.stream.Collectors;

/**
 * Wants to make an expanding square castle... however if opponents interfere then will reluctantly make an odd-shaped castle   
 */
public class Castler {
    private static final int MAP_SIZE = 10;
    private int ownId;
    private int deployableArmyCount;
    private List<Territory> territories;
    private Territory[][] map;
    private Map<Territory,Territory> territoryHashMap;
    List<Territory> ownedTerritories;
    public int minRow;
    public int minCol;

    public static void main(String[] args)
    {
        new Castler(args);
    }

    Castler(String[] args)
    {
        ownId = Integer.parseInt(args[0]);
        deployableArmyCount = Integer.parseInt(args[1]);

        territories = new ArrayList<Territory>();
        map = new Territory[MAP_SIZE][MAP_SIZE]; 

        territoryHashMap = new HashMap<Territory,Territory>();

        for (String s : args[2].split(" ")) {
            Territory territory = new Territory(s.split(","));
               territories.add(territory);
            territoryHashMap.put(territory, territory);
            map[territory.col][territory.row]=territory;
        }

        ownedTerritories = territories.stream().filter(t->t.id==ownId).collect(Collectors.toList());

        minRow=Integer.MAX_VALUE;
        minCol=Integer.MAX_VALUE;

        //find top left territory that is the corner of our castle :)
        int largestArea=0;
        for (Territory territory : ownedTerritories)
        {
            int area=countRightDownConnected(territory,new int[MAP_SIZE][MAP_SIZE]);
            if (area>largestArea)
            {
                largestArea=area;
                minRow=territory.row;
                minCol=territory.col;
            }
        }

        // the average army size per owned territory
           int meanArmySize=0;
           for (Territory territory : ownedTerritories)
           {
               meanArmySize+=territory.armies;
           }
           meanArmySize/=ownedTerritories.size();


        int squareSideLength = (int) Math.ceil(Math.sqrt(ownedTerritories.size()));

        // if we own all territories inside the square of our castle, or we have stalled but have the numbers to expand... make the length of side of the square larger to allow expansion
        if (squareSideLength*squareSideLength == ownedTerritories.size() || meanArmySize>squareSideLength)
        {
            squareSideLength++;
        }

        // lets collate all the enemy territories within the area of our desired castle square and marke them as candidates to be attacked.
        List<Territory> attackCandidates = new ArrayList<>();
        for (int y=minRow;y<minRow+squareSideLength;y++)
        {
            for (int x=minCol;x<minCol+squareSideLength;x++)
            {
                Territory territory = map[x%MAP_SIZE][y%MAP_SIZE];
                if (territory!=null && territory.id!=ownId)
                {
                    attackCandidates.add(territory); 
                }
            }
        }


        // sort in ascending defensive army size.
        attackCandidates.sort((a,b)->a.armies-b.armies);

        List<Territory> unCommandedTerritories = new ArrayList<>(ownedTerritories);
        List<Move> moves = new ArrayList<>();
        Set<Territory> suicideAttackCandidate = new HashSet<>();

        // command owned territories to attack any territories within the area of the prescribed square if able to win. 
        for (int i=0;i<unCommandedTerritories.size();i++)
        {
            Territory commandPendingTerritory =unCommandedTerritories.get(i);
            List<Territory> neighbours = getNeighbours(commandPendingTerritory,map);
            List<Territory> attackCandidatesCopy = new ArrayList<>(attackCandidates);

            // remove non-neighbour attackCandidates
            attackCandidatesCopy.removeIf(t->!neighbours.contains(t));

            for (Territory attackCandidate : attackCandidatesCopy)
            {
                Battle battle = battle(commandPendingTerritory,attackCandidate);
                if (battle.attackerWon)
                {
                    attackCandidates.remove(attackCandidate);
                    suicideAttackCandidate.remove(attackCandidate);
                    unCommandedTerritories.remove(i--);

                    Territory[][] futureMap = cloneMap(map);
                    futureMap[attackCandidate.col][attackCandidate.row].id=ownId;

                    // default to sending the required armies to win + half the difference of the remainder
                    int armiesToSend = battle.minArmiesRequired + (commandPendingTerritory.armies-battle.minArmiesRequired)/2;

                    // but if after winning, there is no threat to the current territory then we shall send most of the armies to attack
                    if (!underThreat(commandPendingTerritory, futureMap))
                    {
                        armiesToSend = commandPendingTerritory.armies-1;
                    }
                    moves.add(new Move(commandPendingTerritory,attackCandidate,armiesToSend));

                    break;
                }
                else
                {
                    // we can't win outright, add it to a list to attack kamikaze style later if needed.
                    suicideAttackCandidate.add(attackCandidate);
                }
            }
        }


        // Find edge territories.
        // A territory is deemed an edge if at least one of its neighbours are not owned by us.
        List<Territory> edgeTerritories = new ArrayList<>();
        ownedTerritories.forEach(owned->
            getNeighbours(owned,map).stream().filter(neighbour->
                neighbour.id!=ownId).findFirst().ifPresent(t->
                edgeTerritories.add(owned)));

        // All edge territories that have not yet had orders this turn...
        List<Territory> uncommandedEdgeTerritories = edgeTerritories.stream().filter(t->unCommandedTerritories.contains(t)).collect(Collectors.toList());

        // Find edges that are under threat by hostile neighbours
        List<Territory> threatenedEdges = edgeTerritories.stream().filter(edge->underThreat(edge,map)).collect(Collectors.toList());

        // All threatened edge territories that have not yet had orders this turn...
        List<Territory> uncommandedThreatenedEdges = threatenedEdges.stream().filter(t->unCommandedTerritories.contains(t)).collect(Collectors.toList());

        // unthreatened edges
        List<Territory> unThreatenedEdges = edgeTerritories.stream().filter(edge->!threatenedEdges.contains(edge)).collect(Collectors.toList());
        List<Territory> uncommandedUnThreatenedEdges = unThreatenedEdges.stream().filter(t->unCommandedTerritories.contains(t)).collect(Collectors.toList());

        // map that describes the effect of moves. Ensures that we do not over commit on one territory and neglect others
        Territory[][] futureMap = cloneMap(map);

        //sort the threatened edges in ascending order of defense
        threatenedEdges.sort((a,b)->a.armies-b.armies); 

        int meanThreatenedEdgeArmySize = Integer.MAX_VALUE;
        if (!threatenedEdges.isEmpty())
        {
            // calculate the average defense of the threatened edges
            int[] total = new int[1];
            threatenedEdges.stream().forEach(t->total[0]+=t.armies);
            meanThreatenedEdgeArmySize = total[0]/threatenedEdges.size(); 

            // command any unthreatened edges to bolster weak threatened edges. 
            out:
            for (int i=0;i<uncommandedUnThreatenedEdges.size();i++)
            {
                Territory commandPendingTerritory = uncommandedUnThreatenedEdges.get(i);

                // the unthreatened edge has spare armies
                if (commandPendingTerritory.armies>1)
                {
                    for (int x=MAP_SIZE-1;x<=MAP_SIZE+1;x++)
                    {
                        for (int y=MAP_SIZE-1;y<=MAP_SIZE+1;y++)
                        {
                            if (!(x==MAP_SIZE && y==MAP_SIZE))
                            {
                                int xx=commandPendingTerritory.col+x;
                                int yy=commandPendingTerritory.row+y;
                                Territory territory = futureMap[xx%MAP_SIZE][yy%MAP_SIZE];

                                // if the current threatened edge has less than average defensive army then send all spare troops to from the uncommanded unthreatened edge. 
                                if (territory!=null && territory.armies<meanThreatenedEdgeArmySize && threatenedEdges.contains(territory))
                                {
                                    // update future map
                                    Territory clonedTerritory = (Territory) territory.clone();
                                    clonedTerritory.armies+=commandPendingTerritory.armies-1;
                                    futureMap[xx%MAP_SIZE][yy%MAP_SIZE]=clonedTerritory;

                                    moves.add(new Move(commandPendingTerritory,territory,commandPendingTerritory.armies-1));

                                    unCommandedTerritories.remove(commandPendingTerritory);
                                    uncommandedUnThreatenedEdges.remove(i--);
                                    uncommandedEdgeTerritories.remove(commandPendingTerritory);
                                    continue out;
                                }
                            }
                        }
                    }
                }
            }

            // command any stronger threatened edges to bolster weak threatened edges. 
            out:

            for (int i=0;i<uncommandedThreatenedEdges.size();i++)
            {
                Territory commandPendingTerritory = uncommandedThreatenedEdges.get(i);

                // the threatened edge has more than average edge armies
                if (commandPendingTerritory.armies>meanThreatenedEdgeArmySize)
                {
                    for (int x=MAP_SIZE-1;x<=MAP_SIZE+1;x++)
                    {
                        for (int y=MAP_SIZE-1;y<=MAP_SIZE+1;y++)
                        {
                            if (!(x==MAP_SIZE && y==MAP_SIZE))
                            {
                                int xx=commandPendingTerritory.col+x;
                                int yy=commandPendingTerritory.row+y;
                                Territory territory = futureMap[xx%MAP_SIZE][yy%MAP_SIZE];

                                // if the current threatened edge has less than average defensive army then send the excess troops larger than the average edge armies amount from the uncommanded threatened edge. 
                                if (territory!=null && territory.armies<meanThreatenedEdgeArmySize && threatenedEdges.contains(territory))
                                {
                                    // update future map
                                    Territory clonedTerritory = (Territory) territory.clone();
                                    clonedTerritory.armies+=commandPendingTerritory.armies-meanThreatenedEdgeArmySize;
                                    futureMap[xx%MAP_SIZE][yy%MAP_SIZE]=clonedTerritory;
                                    moves.add(new Move(commandPendingTerritory,territory,commandPendingTerritory.armies-meanThreatenedEdgeArmySize));

                                    unCommandedTerritories.remove(commandPendingTerritory);
                                    uncommandedThreatenedEdges.remove(i--);
                                    uncommandedEdgeTerritories.remove(commandPendingTerritory);
                                    continue out;
                                }
                            }
                        }
                    }
                }
            }
        }

        // for any uncommanded non-edge territories, just move excess armies to the right or down
           unCommandedTerritories.stream().filter(t->
               t.armies>1 && !edgeTerritories.contains(t)).forEach(t->
                   moves.add(new Random().nextFloat()>0.5? (new Move(t,map[(t.col+1)%MAP_SIZE][t.row],t.armies-1)):(new Move(t,map[t.col][(t.row+1)%MAP_SIZE],t.armies-1))));



           // lets perform suicide attacks if we are in a good position to do so... hopefully will whittle down turtling enemies.
        for (Territory target : suicideAttackCandidate)
        {
            List<Territory> ownedNeighbours = getNeighbours(target, map).stream().filter(neighbour->neighbour.id==ownId).collect(Collectors.toList());

            for (Territory ownedTerritory : ownedNeighbours)
            {
                // if the edge has yet to be commanded and the territory has more than three times the average armies then it is likely that we are in a power struggle so just suicide attack!
                if (uncommandedEdgeTerritories.contains(ownedTerritory) && ((ownedTerritory.armies)/3-1)>meanArmySize)
                {
                    uncommandedEdgeTerritories.remove(ownedTerritory);
                    unCommandedTerritories.remove(ownedTerritory);
                    moves.add(new Move(ownedTerritory,target,ownedTerritory.armies-meanArmySize));
                }
            }
        }


        // deploy troops to the weakest threatened edges
        int armiesToDeploy =deployableArmyCount;

        Map<Territory,Integer> deployTerritories = new HashMap<>();
        while (armiesToDeploy>0 && threatenedEdges.size()>0)
        {
            for (Territory threatenedEdge : threatenedEdges)
            {
                Integer deployAmount = deployTerritories.get(threatenedEdge);
                if (deployAmount==null)
                {
                    deployAmount=0;
                }
                deployAmount++;
                deployTerritories.put(threatenedEdge,deployAmount);
                armiesToDeploy--;
                if (armiesToDeploy==0) break;
            }
        }

        // no threatened edges needing deployment, so just add them to the "first" edge
        if (armiesToDeploy>0)
        {
            deployTerritories.put(edgeTerritories.get(new Random().nextInt(edgeTerritories.size())),armiesToDeploy);
        }

        // send deploy command
        StringBuilder sb = new StringBuilder();
        deployTerritories.entrySet().stream().forEach(entry-> sb.append(entry.getKey().row + "," + entry.getKey().col + "," + entry.getValue()+" "));
        sb.append(" ");
        System.out.println(sb);

        StringBuilder sb1 = new StringBuilder();

        // send move command
        moves.stream().forEach(move-> sb1.append(move.startTerritory.row + "," + move.startTerritory.col + "," + move.endTerritory.row + "," + move.endTerritory.col + "," + move.armies+" "));
        sb1.append(" ");
        System.out.println(sb1);

    }

    /**
     *    Recursive method that attempts to count area the territories in the square with the given territory as the top left corner  
     */
    private int countRightDownConnected(Territory territory,int[][] visited) {

        int count=0;
        if (visited[territory.col][territory.row]>0) return visited[territory.col][territory.row];
        if (visited[territory.col][territory.row]<0) return 0;
        visited[territory.col][territory.row]=-1;


        if (territory!=null && territory.id==ownId)
        {
            if (visited[territory.col][territory.row]>0) return visited[territory.col][territory.row];

            count++;
            count+=countRightDownConnected(map[territory.col][(territory.row+1)%MAP_SIZE],visited);
            count+=countRightDownConnected(map[(territory.col+1)%MAP_SIZE][territory.row],visited);
            visited[territory.col][territory.row]=count;
        }
        return count;
    }

    /**
     *    Performs a deep clone of the provided map  
     */
    private Territory[][] cloneMap(Territory[][] map)
    {
        Territory[][] clone = new Territory[MAP_SIZE][MAP_SIZE];
        for (int x=0;x<MAP_SIZE;x++)
        {
            for (int y=0;y<MAP_SIZE;y++)
            {
                Territory territory = map[x][y];
                clone[x][y] = territory==null?null:territory.clone();
            }
        }
        return clone;
    }

    /**
     * Simulates a battle between an attacker and a defending territory
     */
    private Battle battle(Territory attacker, Territory defender) 
    {
        Battle battle = new Battle();
        battle.attackerWon=false;
        battle.loser=attacker;
        battle.winner=defender;

        for (int i=0;i<attacker.armies;i++)
        {
            int attackerArmies = i;
            int defenderArmies = defender.armies;
            defenderArmies -= (int) Math.round(attackerArmies * .6);
            attackerArmies -= (int) Math.round(defenderArmies * .7);
            if (defenderArmies <= 0) {
                if (attackerArmies > 0) {
                    defenderArmies = attackerArmies;
                    battle.attackerWon=true;
                    battle.loser=defender;
                    battle.winner=attacker;
                    battle.minArmiesRequired=i;
                    break;
                }
            }
        }
        return battle;
    }

    /**
     * returns true if the provided territory is threatened by any hostile neighbours using the provided map 
     */
    private boolean underThreat(Territory territory,Territory[][] map)
    {
        return !getNeighbours(territory,map).stream().filter(neighbour->neighbour.id!=ownId && neighbour.id!=-1).collect(Collectors.toList()).isEmpty();
    }

    /**
     * returns the neighbours of the provided territory using the provided map 
     */
    private List<Territory> getNeighbours(Territory territory,Territory[][] map) {

        List<Territory> neighbours = new ArrayList<>();
        for (int x=MAP_SIZE-1;x<=MAP_SIZE+1;x++)
        {
            for (int y=MAP_SIZE-1;y<=MAP_SIZE+1;y++)
            {
                if (!(x==MAP_SIZE && y==MAP_SIZE))
                {
                    Territory t = map[(x+territory.col)%MAP_SIZE][(y+territory.row)%MAP_SIZE];
                    if (t!=null) neighbours.add(t);
                }
            }
        }
        return neighbours;
    }

    static class Battle {
        public int minArmiesRequired;
        Territory winner;
        Territory loser;
        boolean attackerWon;
    }

    static class Move
    {
        public Move(Territory startTerritory, Territory endTerritory, int armiesToSend) 
        {
            this.endTerritory=endTerritory;
            this.startTerritory=startTerritory;
            this.armies=armiesToSend;
        }
        Territory startTerritory;
        Territory endTerritory;
        int armies;
    }

    static class Territory implements Cloneable
    {
        public int id, row, col, armies;

        public Territory clone()
        {
            try {
                return (Territory) super.clone();
            } catch (CloneNotSupportedException e) {
                throw new RuntimeException(e);
            }
        }

        public Territory(String[] data) {
            id = Integer.parseInt(data[3]);
            row = Integer.parseInt(data[0]);
            col = Integer.parseInt(data[1]);
            armies = Integer.parseInt(data[4]);
        }

        void add(Territory territory)
        {
            row+=(territory.row);
            col+=(territory.col);
        }

        @Override
        public int hashCode()
        {
            return row*MAP_SIZE+col;
        }

        @Override
        public boolean equals(Object other)
        {
            Territory otherTerritory = (Territory) other;
            return row == otherTerritory.row && col == otherTerritory.col;
        }

    }
}

4

ฤาษี - ชวา

แค่เพิ่มกองทัพของเขาไปยังเมืองเดียวกัน ฉันไม่คิดว่ามันจะถูกถอดออกได้หากไม่ได้รับโบนัส

public class Hermit {
    public static void main(String[] args) {
        int myId = Integer.parseInt(args[0]);

        for (String s : args[2].split(" ")) {
            String[] data = s.split(",");
            int id = Integer.parseInt(data[3]);
            int row = Integer.parseInt(data[0]);
            int col = Integer.parseInt(data[1]);

            if (id == myId) {
                System.out.println(row + "," + col + "," + args[1]);
                break;
            }
        }
        System.out.println();
    }
}

ความเรียบง่ายนั้นมีประสิทธิภาพอย่างน่าประหลาดใจ! :) ทำได้ดีมาก
Moogie

4

WeSwarm - C ++ 11 [v2.2]

อัปเดตเป็น v2.2 ตั้งแต่วันที่ 25 สิงหาคม 2558

v2.2 - ปรับเนื่องจากการเปลี่ยนแปลงในวิธีที่ตัวควบคุมรายงานกองทัพ

v2.1 - stoiทีเอ็นทีมีปัญหารวบรวมรหัสของฉันดังนั้นฉันหยุดใช้

v2.0 - รหัส refactor พร้อมกับแก้ไขข้อบกพร่องเล็กน้อย


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

#include <cstdlib>
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <set>
#include <cmath>
#include <algorithm>
#include <map>

using namespace std;

/// http://stackoverflow.com/questions/236129/split-a-string-in-c
vector<string> &split(const string &s, char delim, vector<string> &elems) 
{
    stringstream ss(s);
    string item;
    while (getline(ss, item, delim)) {
        elems.push_back(item);
    }
    return elems;
}

/// http://stackoverflow.com/questions/236129/split-a-string-in-c
vector<string> split(const string &s, char delim)
{
    vector<string> elems;
    split(s, delim, elems);
    return elems;
}

enum Allegiance { MINE, ENEMY, HOSTILE, NPC, ANY };

class Bonus
{
public:
    Bonus(int id, int armies, int territoriesLeft)
    {
        this->id = id;
        this->armies = armies;
        this->territoriesLeft = territoriesLeft;
    }

    int getId()
    {
        return id;
    }

    int getArmies()
    {
        return armies;
    }

    int getTerritoriesLeft()
    {
        return territoriesLeft;
    }

private:
    /// id of the bonus.
    int id;

    /// number of extra armies that this bonus gives.
    int armies;

    /// number of territories in the bonus that still needs to be captured.
    int territoriesLeft;
};

class Territory
{
public:
    Territory(int row, int col, Bonus* bonus, int playerId, int armies, Allegiance allegiance)
    {
        this->row = row;
        this->col = col;
        this->bonus = bonus;
        this->armies = armies;
        this->allegiance = allegiance;
        this->toAdd = 0;
        this->toRemove = 0;
    }

    Territory(Territory *territory)
    {
        this->row = territory->getRow();
        this->col = territory->getCol();
        this->bonus = territory->getBonusPtr();
        this->armies = territory->getArmies();
        this->allegiance = ANY;
        this->toAdd = 0;
        this->toRemove = 0;
    }

    /// Ensures uniqueness
    bool operator<(const Territory& other) const
    {
        return row < other.row && + col < other.col;
    }

    /// Return the minimum number of armies needed to conquer this territory.
    int conquerNeeded()
    {
        /*
        Say a player sends n armies from his/her territory to an opposing territory with o armies in it. 
        o will decrease by n * .6 rounded to the nearest integer; 
        however, at the same time, n will decrease by o * .7 rounded to the nearest integer. 
        The following rules dealing with whether or not the opposing territory has been captured will apply:

        If o reaches zero AND n is greater than 0, the player will take over the territory, which will have n armies in it.
        If both n and o become zero, o will automatically be set to 1 and the territory will not be captured.
        If o remains greater than 0, the number of armies in the player's territory will increase by n and the opposing territory will not be captured.
        */

        int o = this->armies; // Given o.
        int n; // Solve for n.
        int n1;
        int n2;

        if (this->allegiance != NPC) {
            o = o + 5; // To account for potential reinforcement.
        }

        // resulto = o - 0.6n
        // resultn = n - 0.7o
        //
        // We want a result of o = 0 and n = 1.
        // 0 = o - 0.6n
        // 1 = n - 0.7o
        // 
        // Isolate n
        // 0.6n = o
        // n = o / 0.6
        n1 = (int)ceil(o / 0.6);
        // 0.7o = n - 1
        // 0.7o + 1 = n
        n2 = (int)ceil(0.7 * o + 1);

        // Take the bigger of the two to guarantee o <= 0 and n >= 1
        n = max(n1, n2);
        return n;
    }

    /// Returns the minimum number of armies that must be added to this territory
    /// to ensure that the territory cannot be taken over by an attack with n armies.
    int reinforceNeeded(int n)
    {
        int o = this->armies; // Number of armies we already have.
        int add = 0; // Solve for number of armies we need to add.

        // resulto = o - 0.6n
        // resultn = n - 0.7o
        //
        // We want a result of o = 1 at the very least.
        // 1 = o - 0.6n
        // 1 + 0.6n = o

        int needed = (int)ceil(1 + 0.6 * n);

        // We only need to reinforce if we don't have enough.
        if (o < needed) {
            add = needed - o;
        }

        return add;
    }

    void add(int toAdd)
    {
        if (toAdd > 0) {
            this->toAdd = this->toAdd + toAdd;
        }
    }

    void remove(int toRemove)
    {
        if (toRemove > 0) {
            this->toRemove = this->toRemove + toRemove;
        }
    }

    void deploy()
    {
        this->armies = this->armies + this->toAdd - this->toRemove;
        this->toAdd = 0;
        this->toRemove = 0;
    }

    int getRow() 
    {
        return row;
    }

    int getCol() 
    {
        return col;
    }

    int getArmies()
    {
        return armies;
    }

    int getAvaliableArmies()
    {
        return armies - 1 - toRemove;
    }

    int getToAdd()
    {
        return toAdd;
    }

    bool isToBeDefended()
    {
        return toAdd > 0;
    }

    Bonus getBonus()
    {
        if (bonus != nullptr) {
            return *bonus;
        }

        return Bonus(-1, 1, 100);
    }

    Bonus *getBonusPtr()
    {
        return bonus;
    }

    bool isMine()
    {
        return allegiance == MINE;
    }

    bool isNPC()
    {
        return allegiance == NPC;
    }

private:
    /// Row number of this territory.
    int row;

    /// Column number of this territory.
    int col;

    /// The bonus that this territory is a part of.
    Bonus* bonus;

    /// number of armies contained in the territory.
    int armies;

    /// number of armies to add or send to the territory.
    int toAdd;

    /// number of armies to remove from this territory.
    int toRemove;

    /// Who this territory belongs to.
    Allegiance allegiance;

};

/// Return whether Territory a is a neighbour of Territory b.
bool isNeighbour(Territory *a, Territory *b)
{
    /*
    n n n
    n x n
    n n n
    */

    // A neighbouring territory is where either:
    // row - 1 , col - 1
    // row - 1 , col + 0
    // row - 1 , col + 1
    // row + 0 , col - 1
    // row + 0 , col + 1
    // row + 1 , col - 1
    // row + 1 , col + 0
    // row + 1 , col + 1

    int rowA = a->getRow();
    int colA = a->getCol();
    int rowB = b->getRow();
    int colB = b->getCol();

    // The row and column is the same, so they're the same territory, but not neighbours.
    if (rowA == rowB && colA == colB) {
        return false;
    }

    // The difference of row : row and column : column is no more than 1.
    // e.g. a territory at row 7 will have neighbour at row 6 and 8.
    if (abs(rowA - rowB) <= 1 && abs(colA - colB) <= 1) {
        return true;
    }

    // Special case for wrapping.

    int checkRow = -1;
    int checkCol = -1;

    // Row is at 0. We need to check for 9 and 1.
    // 1 is already covered by 0 - 1. Explicitly check the 0 - 9 case.
    if (rowB == 0) {
        checkRow = 9;
    }

    // Row is at 9. We need to check for 0 and 8.
    // 8 is already covered by 9 - 9. Explicitly check the 9 - 0 case;
    if (rowB == 9) {
        checkRow = 0;
    }

    // Same thing for column
    if (colB == 0) {
        checkCol = 9;
    }


    if (colB == 9) {
        checkCol = 0;
    }

    if ((rowA == checkRow && abs(colA - colB) <= 1) ||
        (abs(rowA - rowB) <= 1 && colA == checkCol) ||
        (rowA == checkRow && colA == checkCol)) {
        return true;
    }

    return false;
}

/// Verify that territory has the correct allegiance.
bool isOfAllegiance(Territory *territory, Allegiance allegiance)
{
    if (allegiance == MINE && territory->isMine()) {
        return true;
    }
    else if (allegiance == ENEMY && !territory->isMine()) {
        // Enemy means NOT mine, which includes NPCs.
        return true;
    }
    else if (allegiance == HOSTILE && !territory->isMine() && !territory->isNPC()) {
        // Specifically enemy PLAYERS.
        return true;
    }
    else if (allegiance == NPC && territory->isNPC()) {
        return true;
    }
    else if (allegiance == ANY) {
        return true;
    }

    return false;
}

/// Return all neighbouring territories of a particular territory,
/// where the neighbouring territories fits the given allegiance.
set<Territory *> getNeighbours(Territory *territory, Allegiance allegiance, set<Territory *> territories)
{
    set<Territory *> neighbours;

    for (Territory *neighbour : territories) {

        if (isNeighbour(neighbour, territory) && isOfAllegiance(neighbour, allegiance)) {
            neighbours.insert(neighbour);
        }

    }

    return neighbours;
}

/// Return the total number of armies near a particular territory that can be mobilized.
int getAvaliableArmiesNear(Territory *territory, Allegiance allegiance, set<Territory *> territories)
{
    int armies = 0;

    set<Territory *> neighbour = getNeighbours(territory, allegiance, territories);

    for (Territory *near : neighbour) {
        armies = armies + near->getAvaliableArmies();
    }

    return armies;
}

/// Return a set of all territories of a particular allegiance.
set<Territory *> getAllTerritories(Allegiance allegiance, set<Territory *> territories)
{
    set<Territory *> t;

    for (Territory *territory : territories) {
        if (isOfAllegiance(territory, allegiance)) {
            t.insert(territory);
        }
    }

    return t;
}

/// Returns the priority of attacking this particular territory.
/// The lower the priority, the better. It is calculated based on
/// the number of territories left to claim a bonus, the number
/// of armies required to take it over, and the number of armies
/// getting this bonus will give us.
int calculateAttackPriority(Territory *territory)
{
    Bonus bonus = territory->getBonus();
    int territoriesLeft = bonus.getTerritoriesLeft();
    int armiesNeeded = territory->conquerNeeded();
    int armiesGiven = bonus.getArmies();
    return (int)round(territoriesLeft * armiesNeeded / armiesGiven);
}

/// Return a map of int, Territories where int represent priority 
/// and Territory is the territory to be attacked.
///
/// Higher priority = LESS important.
///
/// ALL territories that can be attacked will appear in the set.
map<int, Territory *> getAttackCandidates(set<Territory *> territories)
{
    map<int, Territory *> attack;

    set<Territory *> opponents = getAllTerritories(ENEMY, territories);

    for (Territory *territory : opponents) {
        int priority = calculateAttackPriority(territory);

        // Check if the territory is already inserted.
        auto findTerritory = attack.find(priority);
        bool inserted = findTerritory != attack.end();

        // Already inserted, so we decrease the priority until we can insert it.
        while (inserted) {
            priority = priority + 1;
            findTerritory = attack.find(priority);
            inserted = findTerritory != attack.end();
        }

        attack.insert({ priority, territory });

    }

    return attack;
}

/// Returns the priority of defending this particular territory.
/// The lower the priority, the better. It is calculated based on
/// whether or not we have this bonus, number of armies that can
/// potentially take it over, and the number of armies
/// getting this bonus will give us.
int calculateDefendPriority(Territory *territory, set<Territory *> territories)
{
    Bonus bonus = territory->getBonus();
    set<Territory *> enemies = getNeighbours(territory, ENEMY, territories);

    int territoriesLeft = bonus.getTerritoriesLeft();
    int armiesNeeded = territory->reinforceNeeded(getAvaliableArmiesNear(territory, HOSTILE, territories));
    int armiesGiven = bonus.getArmies();

    return (int)round((1 + territoriesLeft) * armiesNeeded / armiesGiven);
}

/// Return a map of int, pair<int, Territory> where int represent priority 
/// and Territory is the territory to be defended.
/// 
/// Again, the higher the priority, the LESS important it is.
///
/// ALL territories that can be defended will appear in the set.
map<int, Territory *> getDefendCandidates(set<Territory *> territories)
{
    map<int, Territory *> defend;

    set<Territory *> mine = getAllTerritories(MINE, territories);

    for (Territory *territory : mine) {
        int priority = calculateDefendPriority(territory, territories);

        // Check if the territory is already inserted.
        auto findTerritory = defend.find(priority);
        bool inserted = findTerritory != defend.end();

        // Already inserted, so we decrease the priority until we can insert it.
        while (inserted) {
            priority = priority + 1;
            findTerritory = defend.find(priority);
            inserted = findTerritory != defend.end();
        }


        defend.insert({ priority, territory });

    }

    return defend;
}


/// Determine which territories to add armies to, and add to them accordingly.
/// Return a set which specifically lists the Territories that will have armies
/// added to them.
///
/// set<Territory> territories is a set of territories that are visible to us.
/// int armies is the number of armies we can add.
set<Territory *> getAdd(set<Territory *> territories, int armies)
{
    set<Territory *> add;

    // First we check whether there are any territories worth defending - i.e. we have bonus.
    map<int, Territory *> defend = getDefendCandidates(territories);

    for (auto pairs : defend) {

        if (armies <= 0) {
            break;
        }

        Territory *territory = pairs.second;

        Bonus bonus = territory->getBonus();

        int need = territory->reinforceNeeded(getAvaliableArmiesNear(territory, HOSTILE, territories));

        // Make sure that we actually need to defend this, and it actually can be defended.
        if (need > 0 && need <= armies + getAvaliableArmiesNear(territory, MINE, territories) + territory->getArmies()) {

            if (need < armies) {
                armies = armies - need;
                territory->add(need);
                add.insert(territory);
            }
            else {
                // Do we really want to use up all our armies
                // if it doen't even give us a bonus?
                if (bonus.getTerritoriesLeft() != 0) {
                    continue;
                }
                territory->add(armies);
                armies = 0;
                add.insert(territory);
            }

        }


    }

    // Attacking is much easier. We simply allocate all the armies
    // to a place beside where we wish to attack. 
    map<int, Territory *> attack = getAttackCandidates(territories);

    for (auto pairs : attack) {

        if (armies <= 0) {
            break;
        }

        Territory *territory = pairs.second;

        // Determine where to allocate.
        set<Territory *> neighbours = getNeighbours(territory, MINE, territories);

        // We'll just arbitrarily pick the first one that is an ally, though any one will work.
        for (Territory *my : neighbours) {

            // I am almost certain I messed up my logic somewhere around here.
            // I'm supposed to initiate an attack if I got a good surround near a territory.
            // However, it isn't working so I removed it and opted for a simpler logic.
            // So far, it is doing well as is. If I start loosing I'll reimplement this ;)
            //int need = territory->conquerNeeded() - getAvaliableArmiesNear(territory, MINE, territories);
            int need = territory->conquerNeeded();
            int a = territory->conquerNeeded();
            int b = getAvaliableArmiesNear(territory, MINE, territories);
            int c = my->getAvaliableArmies();

            if (need <= 0) {
                continue;
            }

            if (need < armies) {
                armies = armies - need;
                my->add(need);
                add.insert(my);
            }
            else {
                my->add(armies);
                armies = 0;
                add.insert(my);
            }
            break;
        }
    }

    // Check if there are any armies left over,
    // because we must add all our armies.
    if (armies > 0) {

        // This means that we are in a perfect position and it doesn't matter where we add it.
        // So we'll just pick a random territory and put it there.
        if (add.size() < 1) {
            set<Territory *> mine = getAllTerritories(MINE, territories);
            auto first = mine.begin();
            Territory *random = *first;
            random->add(armies);
            add.insert(random);
        }
        else {
            // In this case, we just throw it to the highest priority.
            auto first = add.begin();
            Territory *t = *first;
            t->add(armies);
        }

    }

    return add;
}


/// Return a set of set of Territories.
/// Each set have [0] as source and [1] as destination.
/// Number of armies to send will be in destination.
/// add is a list of territories with armies added to them.
set<pair<Territory *, Territory *>> getSend(set<Territory *> territories)
{
    set<pair<Territory *, Territory *>> send;

    // Attacking is much easier. We simply allocate all the armies
    // to a place beside where we wish to attack. 
    map<int, Territory *> attack = getAttackCandidates(territories);

    for (auto pairs : attack) {

        Territory *territory = pairs.second;

        int needed = territory->conquerNeeded();
        set<Territory *> mine = getNeighbours(territory, MINE, territories);

        // Find all our territories avaliable for attack.
        for (Territory *my : mine) {

            // We need to make sure we actually have enough!
            int avaliable = my->getAvaliableArmies();

            // We send all our attacking army from a single territory,
            // So this one territory must have enough.
            if (needed > 0 && avaliable >= needed) {
                // Attack!
                territory->add(needed); // represents number of armies to send.
                my->remove(needed);

                pair<Territory *, Territory *> attackOrder(my, territory); // src -> dst.
                send.insert(attackOrder);   
                break;
            }
        }
    }

    // First we check whether there are any territories worth defending - i.e. we have bonus.
    map<int, Territory *> defend = getDefendCandidates(territories);

    for (auto pairs : defend) {

        Territory *territory = pairs.second;

        // Number of armies that will potentially attack.
        int threat = getAvaliableArmiesNear(territory, HOSTILE, territories);

        // The number of armies needed to reinforce this attack.
        int needed = territory->reinforceNeeded(threat);

        if (needed <= 0) {
            continue;
        }

        // Check that we have enough to actually defend.
        int avaliable = getAvaliableArmiesNear(territory, MINE, territories);
        set<Territory *> neighbours = getNeighbours(territory, MINE, territories);

        if (avaliable < needed) {
            // Not enough, retreat!
            for (Territory *my : neighbours) {

                int retreat = territory->getAvaliableArmies();
                if (retreat > 0) {
                    // Retreat!

                    // Remove from the territory in defense candidate.
                    territory->remove(retreat);

                    // Add to territory else where.
                    my->add(retreat);

                    pair<Territory *, Territory *> defendOrder(territory, my); // src -> dst.
                    send.insert(defendOrder);
                }
                // We retreat to a single territory.
                // So we break as soon as we find a territory.
                // If there is no territory, this loop won't run.
                break;
            }
        }
        else {

            // Track how many we still need to add.
            int stillneed = needed;

            // Reinforce!
            for (Territory *my : neighbours) {

                // Do we need more?
                if (stillneed <= 0) {
                    break;
                }

                // Check that it's not about to be reinforced.
                // Otherwise, it is senseless to take armies away from a
                // territory we intend to defend!
                if (!my->isToBeDefended()) {
                    int canSend = my->getAvaliableArmies(); 
                    if (canSend > 0) {
                        // Reinforce!

                        // We create a copy of the territory when adding.
                        // Why? Because in this case, the destination Territory
                        // is only meant as a place holder territory simply
                        // for the purpose of having the toAdd value read.
                        Territory *territoryAdd = new Territory(territory);
                        territoryAdd->add(canSend);

                        // Remove from the territory we are sending from.
                        my->remove(canSend);

                        stillneed = stillneed - canSend;

                        pair<Territory *, Territory *> defendOrder(my, territoryAdd); // src -> dst.
                        send.insert(defendOrder);
                    }                   
                }
            }
        }



    }

    return send;
}

/// Rules of Engagement:
/// 1. Collect Bonuses.
/// 2. Attack Weak Territories whenever possible.
///
/// Rules of Defense:
/// 1. Reinforce if possible.
/// 2. Otherwise, retreat and live to fight another day
///
/// For a given territory, we will prioritze attacking over 
/// defending if we do not have the bonus yet for that territory. 
/// If we have the bonus, we will prioritze defending over attacking.
int main(int argc, char* argv[])
{
    // Note: cannot use stoi because of compilation problems.

    int id = atoi(argv[1]);
    int armies = atoi(argv[2]);
    string territoriesIn = argv[3];
    string bonusesIn = argv[4]; 

    // First seperate by space, then seperate by comma.
    vector<string> territoriesData = split(territoriesIn, ' ');
    vector<string> bonusesData = split(bonusesIn, ' ');

    set<Territory *> territories;
    map<int, Bonus *> bonuses;

    for (string data : bonusesData) {
        // [id],[armies],[territories left]
        vector<string> bonus = split(data, ',');
        int id = atoi(bonus[0].c_str());
        int armies = atoi(bonus[1].c_str());
        int territoriesLeft = atoi(bonus[2].c_str());

        Bonus *b = new Bonus(id, armies, territoriesLeft);

        bonuses.insert({ id, b });
    }

    for (string data : territoriesData) {
        // [row],[col],[bonus id],[player id],[armies]
        vector<string> territory = split(data, ',');

        int row = atoi(territory[0].c_str());
        int col = atoi(territory[1].c_str());
        int bonusId = atoi(territory[2].c_str());
        int playerId = atoi(territory[3].c_str());
        int armies = atoi(territory[4].c_str());

        // We can assume that each territory always belongs to a bonus.
        auto findBonus = bonuses.find(bonusId);
        Bonus *bonus;

        if (findBonus != bonuses.end()) {
            bonus = findBonus->second;
        }
        else {
            bonus = nullptr;
        }

        Allegiance allegiance = ENEMY;
        if (playerId == id) {
            allegiance = MINE;
        }
        else if (playerId == -1) {
            allegiance = NPC;
        }

        Territory *t = new Territory(row, col, bonus, playerId, armies, allegiance);

        territories.insert(t);

    }



    // Here we output our desire to add armies.
    set<Territory *> add = getAdd(territories, armies);

    string delimiter = "";
    for (Territory *t : add) {
        cout << delimiter << t->getRow() << "," << t->getCol() << "," << t->getToAdd();
        delimiter = " ";

        // Move added army to actual army.
        t->deploy();
    }

    cout << endl;

    // Here we output our desire to send armies.
    set<pair<Territory *, Territory *>> send = getSend(territories);

    delimiter = "";

    // Note that if you do not want to move any armies, your program should print a space.
    if (send.size() == 0) {
        cout << " ";
    }
    else {
        for (auto location : send) {
            Territory *source = location.first;
            Territory *destination = location.second;

            cout << delimiter << source->getRow() << "," << source->getCol() << "," << destination->getRow() << "," << destination->getCol() << "," << destination->getToAdd();
            delimiter = " ";
        }
    }

    cout << endl;

    return 0;
}

Animated GIF

Animated GIF สำหรับ v2.2

เก็บถาวร:

v2.1: https://drive.google.com/uc?export=download&id=0B-BtKdd4FDDEU3lkNzVoTUpRTG8

v1.0: https://drive.google.com/uc?export=download&id=0B-BtKdd4FDDEVzZUUlFydXo2T00


ขอบคุณ! น่าเสียดายที่ฉันไม่สามารถรวบรวมโปรแกรมของคุณเนื่องจากstoiไม่ได้รับการแก้ไขแม้จะมี C ++ 11 มีการสอดคล้องปัญหา กับ การแก้ไข มันซึ่งผมยังไม่ได้คิดออกว่าจะทำอย่างไรเพื่อให้คุณสามารถให้เป็นโซลูชั่นสำรองที่ไม่ได้ใช้stoi?
TNT

@TNT Ahh ที่ดูด ฉันยังใหม่กับ C ++ แต่ฉันแน่ใจว่าฉันสามารถหาอะไรบางอย่างได้
Zsw

@TNT โปรดดูว่าคุณสามารถรวบรวมได้ตอนนี้
Zsw

มันรวบรวมและทำงานได้ดี ขอบคุณ!
TNT

@Zsw WeSwarm แข็งแกร่งมาก การทำงานที่ดี! ฉันจะต้องดูว่าฉันจะสามารถรับมือกับกลยุทธ์ต่อต้านได้ไหม: P
Moogie

3

LandGrab - Java

ยิ่งดินแดนยิ่งดี กำหนดเป้าหมายเป็นเขตแดนฟรีเฉพาะหากมีจากนั้นด้วยกองทัพที่เหลือจะเริ่มสร้างและนำศัตรูทีละหนึ่งไทล์

import java.util.Arrays;
import java.util.LinkedList;



public class LandGrab {
    public static void main(String[] args) {

        //Init
        int id = Integer.parseInt(args[0]);
        int armies = Integer.parseInt(args[1]);
        LinkedList<Territory> myTerritories = new LinkedList<Territory>();
        LinkedList<Territory> enemyTerritories = new LinkedList<Territory>();
        LinkedList<Territory> freeTerritories = new LinkedList<Territory>();
        for (String s : args[2].split(" ")) {
            Territory t = new Territory(s.split(","));
            if (t.id == id)
                myTerritories.add(t);
            else if(t.id == -1)
                freeTerritories.add(t);
            else
                enemyTerritories.add(t);
        }

        LinkedList<int[]> deploy = new LinkedList<int[]>();
        LinkedList<int[]> move = new LinkedList<int[]>();

        //Boost up territories next to free ones
        for(Territory mine : myTerritories){
            if(armies <= 0) break;
            LinkedList<Territory> neighbors = getNeighbors(mine, freeTerritories);
            int depArm = 0;
            while(neighbors.peek() != null && armies * 0.6 >= neighbors.peek().armies){
                Territory x = neighbors.pop();
                int needed = x.armies * 2;
                depArm += needed;
                mine.armies += needed;
                armies -= needed;
                int[] temp = {mine.row, mine.col, x.row, x.col, needed};
                move.add(temp);
            }
            int[] temp = {mine.row, mine.col, depArm};
            if(depArm > 0) deploy.add(temp); 
        }

     /* //Take any freebies we can
        for(Territory mine : myTerritories){
            LinkedList<Territory> neighbors = getNeighbors(mine, freeTerritories);
            while(neighbors.peek() != null){
                Territory x = neighbors.pop();
                if((mine.armies - 1) > x.armies * 2){
                    int needed = x.armies * 2;
                    move += mine.row + "," + mine.col + "," + x.row + "," + x.col + "," + (needed) + " ";
                    mine.armies -= needed;
                }
            }
        }
       */ 
        //Choose a single enemy army and crush it
        if(enemyTerritories.size() > 0 && armies > 0){
            Territory x = enemyTerritories.pop();
            Territory y = largest(getNeighbors(x, myTerritories));
            int[] temp = {y.row, y.col, armies};
            deploy.add(temp);
            int armSize = y.armies + armies - 1;
            if(armSize * 0.6 > x.armies){
                int[] attack = {y.row, y.col, x.row, x.col, armSize};
                move.add(attack);
            }
            armies = 0;
        }

        //Deploy leftover armies wherever
        if(armies > 0){
            Territory rand = myTerritories.getFirst();
            int[] temp = {rand.row, rand.col, armies};
            deploy.add(temp); 
        }

        //Consolidate
        String deployString = consolidate(deploy);
        String moveString = "";
        for(int[] command : move){
            moveString += Arrays.toString(command).replace(" ", "").replace("[", "").replace("]", "") + " ";
        }
        if(moveString == "") moveString = " ";

        //Return
        System.out.println(deployString);
        System.out.println(moveString);





    }


    private static Territory largest(LinkedList<Territory> l){
        Territory largest = l.getFirst();
        for(Territory t : l){
            if(t.armies > largest.armies) largest = t;
        }
        return largest;
    }

    public static String consolidate(LinkedList<int[]> list){
        LinkedList<int[]> combined = new LinkedList<int[]>();
        for(int[] t : list){
            boolean dup = false;
            for(int[] existing : combined){
                if(t[0] == existing[0] && t[1] == existing[1]){
                    existing[2] += t[2];
                    dup = true;
                }

            }
            if(!dup) combined.add(t);
        }

        String result = "";
        for(int[] dep : combined){
            result += Arrays.toString(dep).replace(" ", "").replace("[", "").replace("]", "") + " ";

        }
        return result;
    }

    private static LinkedList<Territory> getNeighbors(Territory t, LinkedList<Territory> possibles){
        LinkedList<Territory> neighbors = new LinkedList<Territory>();
        for(Territory x : possibles){
            if(Math.abs(x.row - t.row) <= 1 && Math.abs(x.col - t.col) <= 1){
                neighbors.add(x);
            }
        }
        return neighbors;
    }

    static class Territory {
        int id, row, col, armies;

        public Territory(String[] data) {
            id = Integer.parseInt(data[3]);
            row = Integer.parseInt(data[0]);
            col = Integer.parseInt(data[1]);
            armies = Integer.parseInt(data[4]);
        }
    }
}

ฉันได้เพิ่มการแก้ไขบอทของคุณเพื่อป้องกันดัชนีจากข้อยกเว้น โปรดยอมรับหรือไม่สนใจการแก้ไข
Moogie

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

หวงแหน! บอทของคุณสามารถกู้คืนจากการสูญเสียดินแดนได้อย่างเชี่ยวชาญ การทำงานที่ดี.
Moogie

3

Halver สุ่ม - Java 8

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

แม้ว่ามันจะไม่สามารถแข่งขันกับ Castler ได้ แต่ก็ดีกับผู้เล่นและบอทอื่น ๆ

import java.util.*;
import java.util.stream.Collectors;


/**
 * Sends half its force to a random territory around itself.   
 */
public class RandomHalver {
    private static final int MAP_SIZE = 10;
    private int ownId;
    private int deployableArmyCount;
    private List<Territory> territories;
    private Territory[][] map;
    private Map<Territory,Territory> territoryHashMap;
    List<Territory> ownedTerritories;
    public int minRow;
    public int minCol;

    public static void main(String[] args)
    {
        new RandomHalver(args);
    }

    RandomHalver(String[] args)
    {
        ownId = Integer.parseInt(args[0]);
        deployableArmyCount = Integer.parseInt(args[1]);

        territories = new ArrayList<Territory>();
        map = new Territory[MAP_SIZE][MAP_SIZE]; 

        territoryHashMap = new HashMap<Territory,Territory>();

        for (String s : args[2].split(" ")) {
            Territory territory = new Territory(s.split(","));
            territories.add(territory);
            territoryHashMap.put(territory, territory);
            map[territory.col][territory.row]=territory;
        }

        ownedTerritories = territories.stream().filter(t->t.id==ownId).collect(Collectors.toList());

        List<Move> moves = new ArrayList<>();

        ownedTerritories.stream().forEach(t->moves.add(new Move(t, getNeighbours(t,map).get(new Random().nextInt(getNeighbours(t,map).size())),t.armies/2)));
        Map<Territory,Integer> deployTerritories = new HashMap<>();
        deployTerritories.put(ownedTerritories.get(new Random().nextInt(ownedTerritories.size())),deployableArmyCount);


        // send deploy command
        StringBuilder sb = new StringBuilder();
        deployTerritories.entrySet().stream().forEach(entry-> sb.append(entry.getKey().row + "," + entry.getKey().col + "," + entry.getValue()+" "));
        sb.append(" ");
        System.out.println(sb);

        StringBuilder sb1 = new StringBuilder();

        // send move command
        moves.stream().filter(m->m.armies>0).forEach(move-> sb1.append(move.startTerritory.row + "," + move.startTerritory.col + "," + move.endTerritory.row + "," + move.endTerritory.col + "," + move.armies+" "));
        sb1.append(" ");
        System.out.println(sb1);

    }


    /**
     * returns the neighbours of the provided territory using the provided map 
     */
    private List<Territory> getNeighbours(Territory territory,Territory[][] map) {

        List<Territory> neighbours = new ArrayList<>();
        for (int x=MAP_SIZE-1;x<=MAP_SIZE+1;x++)
        {
            for (int y=MAP_SIZE-1;y<=MAP_SIZE+1;y++)
            {
                if (!(x==MAP_SIZE && y==MAP_SIZE))
                {
                    Territory t = map[(x+territory.col)%MAP_SIZE][(y+territory.row)%MAP_SIZE];
                    if (t!=null) neighbours.add(t);
                }
            }
        }
        return neighbours;
    }

    static class Battle {
        public int minArmiesRequired;
        Territory winner;
        Territory loser;
        boolean attackerWon;
    }

    static class Move
    {
        public Move(Territory startTerritory, Territory endTerritory, int armiesToSend) 
        {
            this.endTerritory=endTerritory;
            this.startTerritory=startTerritory;
            this.armies=armiesToSend;
        }
        Territory startTerritory;
        Territory endTerritory;
        int armies;
    }

    static class Territory implements Cloneable
    {
        public int id, row, col, armies;

        public Territory clone()
        {
            try {
                return (Territory) super.clone();
            } catch (CloneNotSupportedException e) {
                throw new RuntimeException(e);
            }
        }

        public Territory(String[] data) {
            id = Integer.parseInt(data[3]);
            row = Integer.parseInt(data[0]);
            col = Integer.parseInt(data[1]);
            armies = Integer.parseInt(data[4]);
        }

        void add(Territory territory)
        {
            row+=(territory.row);
            col+=(territory.col);
        }

        @Override
        public int hashCode()
        {
            return row*MAP_SIZE+col;
        }

        @Override
        public boolean equals(Object other)
        {
            Territory otherTerritory = (Territory) other;
            return row == otherTerritory.row && col == otherTerritory.col;
        }

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