ต่อสู้เพื่อจาน Petri


32

ในการท้าทายนี้คุณต้องออกแบบสายพันธุ์ของสิ่งมีชีวิตเซลล์เดียวเพื่อต่อสู้กับความตายในเวที petri-dish เวทีถูกแสดงเป็นกริดสี่เหลี่ยมโดยที่แต่ละเซลล์มีหนึ่งช่องว่าง:

.....x....
...x...o..
...x.c..o.
.......o..

แอตทริบิวต์

ทุกเซลล์มีสามคุณลักษณะ เมื่อระบุเซลล์สปีชีส์ของคุณในตอนเริ่มเกมคุณจะต้องจัดสรร 12 คะแนนระหว่างแอตทริบิวต์เหล่านี้

  • Hit Points (HP): หาก HP ของเซลล์ตกลงไปที่ศูนย์มันจะตาย เซลล์ใหม่มี HP เต็ม
    • เมื่อเซลล์ตายมันจะทิ้งไว้ข้างหลังศพซึ่งเซลล์อื่นสามารถกินเป็นพลังงานได้
    • เซลล์ไม่สามารถฟื้น HP ที่หายไปกลับคืนได้ แต่สามารถสร้างเซลล์ใหม่ด้วย HP เต็มรูปแบบโดยการหาร
  • พลังงาน : การกระทำส่วนใหญ่ที่เซลล์สามารถรับได้นั้นต้องการพลังงาน โดยการพักผ่อนอย่างแข็งขันเซลล์สามารถฟื้นพลังงานที่สูญเสียไปจนถึงจำนวนสูงสุดของสปีชีส์
    • เซลล์ชนิดที่มีพลังงานน้อยกว่า 5 น่าจะล้มเหลวเพราะไม่สามารถแบ่งเพื่อสร้างเซลล์ใหม่ได้
    • เซลล์ไม่สามารถฟื้นพลังงานเกินค่าสูงสุดของสปีชีส์ได้
    • เซลล์ที่สร้างขึ้นใหม่มีค่าพลังงานเริ่มต้นคัดลอกมาจากแหล่งกำเนิดของมัน (และค่าสูงสุดกำหนดโดยสเปคของสปีชีส์)
  • ความเป็นกรด : หากเซลล์เลือกที่จะระเบิดระดับความเป็นกรดของเซลล์จะใช้ในการคำนวณความเสียหายต่อเซลล์ที่อยู่ติดกัน

การปฏิบัติ

ทุกเทิร์นทุกเซลล์สามารถดำเนินการอย่างใดอย่างหนึ่ง:

  • ย้าย:เซลล์เคลื่อนที่หนึ่งช่องว่างในทิศทางใดก็ได้ (N / S / E / W / NE / NW / SE / SW) ด้วยต้นทุน 1 พลังงาน

    • เซลล์ไม่สามารถเคลื่อนที่ไปยังพื้นที่ที่ถูกครอบครองโดยเซลล์ที่มีชีวิตอื่น
    • เซลล์ไม่สามารถย้ายออกจากกริด
    • การเคลื่อนที่เข้าสู่เซลล์ศพจะเป็นการทำลายศพ
  • การโจมตี:เซลล์โจมตีเซลล์ที่อยู่ติดกันสร้างความเสียหาย 1 ถึง 3 โดยการเพิ่มคะแนนพลังงาน 1 ถึง 3

    • เซลล์สามารถโจมตีได้ทุกทิศทาง (N / S / E / W / NE / NW / SE / SW)
    • มันถูกกฎหมายในการโจมตีเซลล์ที่เป็นมิตร
  • หาร:เซลล์จะแบ่งและสร้างเซลล์ใหม่ในพื้นที่ที่อยู่ติดกันในราคา 5 พลังงาน

    • เซลล์สามารถแบ่งในทิศทางใดก็ได้ (N / S / E / W / NE / NW / SE / SW)
    • เซลล์ใหม่มี HP เต็มตามข้อกำหนดเซลล์ดั้งเดิมของคุณ
    • เซลล์ใหม่มีพลังงานมากเท่ากับเซลล์แม่หลังจากลบค่าใช้จ่ายในการหาร (ตัวอย่างเช่นเซลล์แม่ที่มีคะแนนพลังงานเริ่มต้น 8 คะแนนจะลดลงเป็น 3 พลังงานและสร้างเซลล์เด็กที่มี 3 พลังงาน)
    • เซลล์ใหม่ไม่สามารถทำหน้าที่ได้จนกว่าคุณจะถึงรอบต่อไป
    • เซลล์ไม่สามารถแบ่งออกเป็นช่องว่างที่ครอบครองโดยเซลล์ที่มีชีวิต แต่สามารถแบ่งออกเป็นช่องว่างที่ครอบครองโดยศพเซลล์ที่ตายแล้ว (สิ่งนี้จะทำลายศพ)
  • Eat:เซลล์กินศพเซลล์ที่อยู่ติดกันได้รับพลังงาน 4 ก้อน

    • เซลล์สามารถกินได้ทุกทิศทาง (N / S / E / W / NE / NW / SE / SW)
  • ส่วนที่เหลือ:เซลล์ไม่ทำอะไรเลยสำหรับหนึ่งเทิร์นคืนพลังงาน 2

  • ระเบิด:เมื่อเซลล์มี HP 3 หรือน้อยกว่าและพลังงานมากกว่า HP มันอาจเลือกที่จะระเบิดสร้างความเสียหายให้กับเซลล์ทั้งแปดที่อยู่ติดกัน

    • สร้างความเสียหายต่อเซลล์แต่ละเซลล์ที่อยู่ติดกัน (exploding cell HP) + (explodng cell acidity)
    • เซลล์ที่ถูกระเบิดจะตายและทิ้งไว้ข้างหลังศพเช่นเดียวกับเซลล์ใด ๆ ที่ถูกฆ่าตายในการระเบิด

โปรโตคอล

ติดตั้ง

โปรแกรมของคุณจะทำงานด้วยสตริงที่BEGINให้ไว้ใน stdin โปรแกรมของคุณจะต้องเขียนไป stdout รายการพื้นที่ที่คั่นของจำนวนเต็ม 3 ที่ไม่ใช่เชิงลบที่เป็นตัวแทนของ HP, พลังงานและความเป็นกรดชนิดมือถือของคุณ: 5 6 1เช่น ตัวเลขต้องรวมเป็น 12 0หากคุณต้องการความเป็นกรด (คุณลักษณะอื่น ๆ อาจเป็นศูนย์ แต่การทำเช่นนั้นจะทำให้เสียเกม!)

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

ทุกเซลล์ทำหน้าที่

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

10 4
..........
..xx.c....
...c...o..
......o...

6 3 5 7

ตัวเลขสองตัวแรกแสดงถึงความกว้างและความสูงของที่เกิดเหตุ: ที่นี่มีสนามขนาด 10 คูณ 4

  • oเซลล์เป็นของคุณ; xเซลล์เป็นศัตรูของคุณ (นี่เป็นเรื่องจริงเสมอผู้เล่นแต่ละคนมักเห็นเซลล์ของตัวเองเป็นo)
  • .พื้นที่ว่างเปล่า
  • cช่องว่างแทนศพเซลล์กิน

ตัวเลขหลังบรรทัดว่างแสดงข้อมูลเกี่ยวกับเซลล์นี้:

  • ตัวเลขสองตัวแรกเป็นx,yพิกัดโดยทำดัชนีจาก0,0ด้านบนซ้าย (ดังนั้น6 3นี่หมายถึงoเซลล์ที่อยู่ใต้สุด)
  • หมายเลขที่สามคือ HP ของเซลล์; ตัวเลขที่สี่คือพลังงานของเซลล์

โปรแกรมของคุณควรส่งออกการกระทำ (ไปยัง stdout) ในตัวอย่างด้านล่างเราจะใช้Nเป็นทิศทางตัวอย่าง แต่อาจเป็นทิศทางที่ถูกต้องตามกฎหมายสำหรับการกระทำนั้น ( N/ S/ E/ W/ NE/ NW/ SE/ / SW) เอาต์พุตของโปรแกรมทั้งหมดไม่คำนึงถึงขนาดตัวพิมพ์ แต่ตัวอย่างจะใช้ตัวพิมพ์ใหญ่ การดำเนินการเอาท์พุทใด ๆ ที่ไม่ถูกต้อง (เนื่องจากมีไวยากรณ์ที่ไม่ถูกต้องหรือพยายามกระทำที่ผิดกฎหมาย) จะถูกละเว้นและผลลัพธ์ในเซลล์RESTไอเอ็นจี (และทำให้ได้รับ 2 พลังงาน)

  • MOVE N
  • DIVIDE N
  • EAT N
  • ATTACK N 2 - ตัวเลขแสดงถึงความแข็งแกร่งของการโจมตี (1 - 3)
  • REST
  • EXPLODE

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

ตัวอย่าง

นี่คือวิธีการทำงานของโปรแกรม อินพุตจาก stdin แสดงที่นี่ด้วย>ลูกศรนำ(แยกออกจากอินพุตจริงด้วยการล้างพื้นที่) และเอาต์พุตบน stdout มี<ลูกศร

> BEGIN
< 5 6 1

จากนั้นโปรแกรมจะถูกเรียกใช้อีกครั้ง:

> 10 4
> ..........
> .o........
> ........x.
> ..........
>
> 1 1 5 6
< DIVIDE SE

หลังจากเทิร์นของคู่ต่อสู้ของคุณ (ผู้ตัดสินใจว่าจะDIVIDE Wใช้เซลล์เริ่มต้นเดียว) โปรแกรมของคุณจะถูกเรียกสองครั้งหนึ่งครั้งสำหรับแต่ละเซลล์:

> 10 4
> ..........
> .o........
> ..o....xx.
> ..........
>
> 1 1 5 1
< MOVE E

สำหรับการขอรอบที่สองในตาคุณ:

> 10 4
> ..........
> ..o.......
> ..o....xx.
> ..........
>
> 2 2 5 1
< MOVE SE

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

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

ชัยชนะ

คุณสามารถชนะโดย:

  • ทำลายเซลล์ที่เป็นปฏิปักษ์ทั้งหมดหรือ
  • มีเซลล์มากกว่าคู่ต่อสู้ของคุณหลังจากที่ผู้เล่นแต่ละคนจบ 150 รอบแล้ว

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

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

ข้อมูลอื่น ๆ

  • โปรแกรมของคุณไม่ควรพยายามรักษาสถานะ (นอกเหนือจากการใช้สถานะของจาน Petri): สิ่งมีชีวิต monocellular ไม่มีความทรงจำที่ดีมากและตอบสนองต่อโลกในแต่ละช่วงเวลา โดยเฉพาะอย่างยิ่งการเขียนไปยังไฟล์ (หรือที่เก็บข้อมูลอื่น) การสื่อสารกับเซิร์ฟเวอร์ระยะไกลหรือการตั้งค่าตัวแปรสภาพแวดล้อมจะไม่ได้รับอนุญาตอย่างชัดเจน
  • ผลงานจะถูกเรียกใช้ / เรียบเรียงบน Ubuntu 12.04.4
  • เฉพาะเกมที่ทำคะแนนได้ 100 เกมนั้นยังไม่ได้รับการยืนยัน แต่น่าจะเกี่ยวข้องกับสนามกีฬาหลายขนาด (ตัวอย่างเช่น 50 เกมบนเวทีเล็ก ๆ และ 50 เกมบนเวทีใหญ่) สำหรับเวทีขนาดใหญ่ฉันอาจเพิ่มจำนวนเทิร์นสูงสุดเพื่อให้แน่ใจว่าการต่อสู้ที่เหมาะสมจะเกิดขึ้น

ทรัพยากร

นี่คือรหัสขับรถที่วิ่งจำลองที่เขียนขึ้นสำหรับ Node.js node petri.js 'first program' 'second program'เรียกได้ว่าเป็น ยกตัวอย่างเช่นบ่อเซลล์หลามเขียนกับมือถือ Java node petri.js 'python some_cell.py' 'java SomeCellClass'เขียนอาจมีลักษณะเช่น

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

แน่นอนคุณมีอิสระที่จะเขียนเซลล์ในภาษาอื่น นี่เป็นเพียงสามภาษาที่ฉันตัดสินใจเขียนรหัสสำเร็จรูปเพื่อช่วยประหยัดเวลา

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


1
@Ryan คุณจะต้องระบุคำสั่งที่รันได้อย่างสมบูรณ์'node c:/cell/cell_template.js'สำหรับแต่ละอาร์กิวเมนต์เช่นเดียวกับที่คุณต้องระบุ'java CellTemplate'สำหรับรหัส Java ฉันจะทำให้ชัดเจนขึ้นในข้อความท้าทาย หากคุณมีปัญหาในสไตล์เรา (และคนอื่น ๆ ที่มีปัญหาทางด้านเทคนิค) สามารถดำเนินการต่อการสนทนานี้ในห้องสนทนาที่ฉันได้ทำเพียง
apsillers

1
@Mogogie เพียง 2 ฝ่ายต่อเกม
apsillers

3
ผู้ชายตัวอย่างที่ยอดเยี่ยม!
CommonGuy

3
@apsillers เราถามคุณในการแชท แต่ลืมที่จะ ping คุณดังนั้นคุณอาจไม่ได้สังเกต: เราสงสัยว่าคุณวางแผนที่จะเล่นเกมเมื่อไหร่?
plannapus

2
@ มนูสุดท้ายใช่! ฉันขอโทษสำหรับความล่าช้านานมาก ฉันมีการตั้งค่ารหัสจับคู่ / การบันทึกคะแนนและตอนนี้ฉันกำลังแก้ไขปัญหาใด ๆ กับการส่งในความพยายามที่จะทำให้รหัสของทุกคนทำงาน หลังจากนั้นฉันจะให้มันทำงานบนเซิร์ฟเวอร์ของฉันเป็นเวลาหนึ่งวันหรือมากกว่านั้นเพื่อทำรอบ
apsillers

คำตอบ:


3

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

def surroundingCells(x, y)
  result = Hash.new
  if x >= 1
    if y >= 1
      # northwest
      result["NW"] = $petriDish[x - 1][y - 1]
    end
    if y < ($sizeY - 1) # $sizeY - 1 is the farthest south square
      # southwest
      result["SW"] = $petriDish[x - 1][y + 1]
    end
      # west
      result["W"] = $petriDish[x - 1][y]
  end
  if x < ($sizeX - 1)
    if y >= 1
      # northeast
      result["NE"] = $petriDish[x + 1][y - 1]
    end
    if y < ($sizeY - 1)
      # southeast
      result["SE"] = $petriDish[x + 1][y + 1]
    end
    # east
    result["E"] = $petriDish[x + 1][y]
  end
  # north
  result["N"] = $petriDish[x][y - 1] if y >= 1
  # south
  result["S"] = $petriDish[x][y + 1] if y < ($sizeY - 1)
  return result
end

def directionTowardsEnemies(locX, locY)
  totalXDirections = 0
  totalYDirections = 0
  totalTargetsFound = 0 # enemies or corpses
  optimalDirections = []
  for x in 0..($petriDish.length - 1)
    for y in 0..($petriDish[0].length - 1)
      if $petriDish[x][y] == 'c' or $petriDish[x][y] == 'x'
        totalXDirections += (x - locX)
        totalYDirections += (y - locY)
        totalTargetsFound += 1
      end
    end
  end
  if totalXDirections == 0
    if totalYDirections > 0
      optimalDirections << "S" << "SE" << "SW"
    else
      optimalDirections << "N" << "NE" << "NW"
    end
    return optimalDirections
  end
  if totalYDirections == 0
    if totalXDirections > 0
      optimalDirections << "E" << "NE" << "SE"
    else
      optimalDirections << "W" << "NW" << "SW"
    end
    return optimalDirections
  end
  if totalXDirections > 0
    if totalYDirections > 0
      optimalDirections << "SE"
      if totalYDirections > totalXDirections
        optimalDirections << "S" << "E"
      else
        optimalDirections << "E" << "S"
      end
    else
      optimalDirections << "NE"
      if -totalYDirections > totalXDirections
        optimalDirections << "N" << "E"
      else
        optimalDirections << "E" << "N"
      end
    end
    return optimalDirections
  end
  if totalXDirections < 0
    if totalYDirections > 0
      optimalDirections << "SW"
      if totalYDirections > -totalXDirections
        optimalDirections << "S" << "W"
      else
        optimalDirections << "W" << "S"
      end
    else
      optimalDirections << "NW"
      if -totalYDirections > -totalXDirections
        optimalDirections << "N" << "W"
      else
        optimalDirections << "W" << "N"
      end
    end
  end
  return optimalDirections
end

firstLine = gets
if firstLine == "BEGIN"
  puts "5 7 0"
  exit 0
end
$sizeX, $sizeY = firstLine.split(' ')[0].to_i, firstLine.split(' ')[1].to_i
$petriDish = Array.new($sizeX) { Array.new($sizeY) }
for y in 0..($sizeY - 1)
  line = gets
  chars = line.split('').reverse.drop(1).reverse # this gets every character but     the last
  for x in 0..(chars.length - 1)
    $petriDish[x][y] = chars[x]
  end
end
gets # blank line
info = gets
locX = info.split(' ')[0].to_i
locY = info.split(' ')[1].to_i
hp = info.split(' ')[2].to_i
energy = info.split(' ')[3].to_i

# dividing is our first priority
if(energy >= 5)
  # try to divide towards enemies
  dirs = directionTowardsEnemies(locX, locY)
  directions = { "N" => [0, -1], "NE" => [1, -1], "E" => [1, 0],
    "SE" => [1, 1], "S" => [0, 1], "SW" => [-1, 1],
    "W" => [-1, 0], "NW" => [-1, -1] }
  for dir in dirs
    potentialNewX = locX + directions[dir][0]
    potentialNewY = locY + directions[dir][1]
    if $petriDish[potentialNewX][potentialNewY] == '.'
      puts "DIVIDE #{dir}"
      exit 0
    end
  end
  # otherwise, just divide somewhere.
  surroundingCells(locX, locY).each do |k, v|
    if v == '.'
      puts "DIVIDE #{k}"
      exit 0
    end
  end
end

# next, eating
surroundingCells(locX, locY).each do |k, v|
  if v == 'c'
    puts "EAT #{k}"
    exit 0
  end
end

# next, attacking
surroundingCells(locX, locY).each do |k, v|
  attackStrength = 0
  if (energy > 5) then # we want to save energy for dividing
    attackStrength = [(energy - 5), 3].min
  else
    attackStrength = [energy, 3].min
  end
  if v == 'x'
    puts "ATTACK #{k} #{attackStrength}"
    exit 0
  end
end

# otherwise, rest
puts "REST"

$ฉันไม่โปรแกรมเมอร์ทับทิมดังนั้นฉันสงสัยว่าทำไมบางตัวแปรเป็นเรื่องปกติและเริ่มต้นบางคนที่มี
seequ

$ใช้เพื่อระบุตัวแปรทั่วโลก ใช่พวกเขาชั่วร้าย แต่ในโปรแกรมขนาดเล็กนี้มันไม่สำคัญมากเกินไป
อเล็กซ์

ตัวแปรทั่วโลกเป็นสิ่งที่ชั่วร้ายในรหัสการผลิตเท่านั้น ใครที่คิดพวกเขาในสคริปต์เช่นนี้
seequ

เซลล์ของฉันเป็นเซลล์เดียวที่มีความสามารถในการแพร่กระจายไม่ใช่ 4-8-0 จริง ๆ หรือ
อเล็กซ์

นี่คือคู่แข่งที่ดีที่สุดสำหรับ CoordinatedBacteria ของฉัน! ฉันสร้างกลยุทธ์ของฉันโดยอ้างอิงจากผลการทดสอบของเซลล์เดี่ยวของคุณ =)
justhalf

3

อะมีบา

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

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

ฉันไม่ได้ทดสอบสิ่งนี้กับสิ่งที่ส่งมาอื่น ๆ ดังนั้นจึงไม่มีความคิดว่าจะทำได้ดีเพียงใด

var MAX_HP = 2;
var MAX_ENERGY = 10;
var ACIDITY = 0;

function PathfindingNode(_x, _y, _prevNode, _distance, _adjacentEnemies) {
    this.x = _x;
    this.y = _y;
    this.prevNode = _prevNode;
    this.distance = _distance;
    this.adjacentEnemies = _adjacentEnemies;
}

PathfindingNode.prototype.GetDistance = function()
{
    return this.distance;
}

var evaluatedNodes = {};
var initialNode = {};
var firstEval = true;

function evaluateNode(x, y, arena)
{
    //get surrounding reachable nodes that havent already been checked
    var adjacentEmpties = arena.getAdjacentMatches(arena.get(x, y), [".", "c"]);

    //if this node is adjacent to the start node - special case because the start node isnt an empty
    if (firstEval)
        adjacentEmpties.push({ 'x': initialNode.x, 'y': initialNode.y });

    //find the optimal node to reach this one
    var prevNode = null;
    for (var i in adjacentEmpties)
    {
        if(evaluatedNodes[adjacentEmpties[i].x + "," + adjacentEmpties[i].y])
        {
            var currentNode = evaluatedNodes[adjacentEmpties[i].x + "," + adjacentEmpties[i].y];

            if (!prevNode) {
                prevNode = currentNode;
            }
            else {
                if(currentNode.GetDistance() < prevNode.GetDistance())
                {
                    prevNode = currentNode;
                }
            }
        }
    }

    var adjacentEnemies = arena.getAdjacentMatches(arena.get(x, y), ["x"]);
    var newNode = new PathfindingNode(x, y, prevNode, prevNode.GetDistance() + 1, adjacentEnemies.length);
    evaluatedNodes[x + "," + y] = newNode;
}

function evaluateNeighbours(arena) {
    //breadth first search all reachable cells
    var nodesToEvaluate = [];
    for (var i in evaluatedNodes) {
        var emptyNodes = arena.getAdjacentMatches(arena.get(evaluatedNodes[i].x, evaluatedNodes[i].y), [".", "c"]);
        //only ones that havent already been eval'd
        for (var j in emptyNodes)
            if (!evaluatedNodes[emptyNodes[j].x + "," + emptyNodes[j].y])
                nodesToEvaluate.push(emptyNodes[j])
    }

    //have all available nodes been evaluated
    if (nodesToEvaluate.length === 0)
        return false;

    for (var i in nodesToEvaluate)
    {
        evaluateNode(parseInt(nodesToEvaluate[i].x), parseInt(nodesToEvaluate[i].y), arena)
    }

    firstEval = false;
    return true;
}

function getAllReachableNodes(arena, cell) {
    //return a list of all reachable cells, with distance and optimal path
    evaluatedNodes = {};

    //add the first node to get started
    var adjacentEnemies = arena.getAdjacentMatches(arena.get(cell.x, cell.y), ["x"]);
    var newNode = new PathfindingNode(parseInt(cell.x), parseInt(cell.y), null, 0, adjacentEnemies.length);
    evaluatedNodes[cell.x + "," + cell.y] = newNode;
    initialNode.x = parseInt(cell.x);
    initialNode.y = parseInt(cell.y);
    firstEval = true;

    while (evaluateNeighbours(arena))
        ;

    return evaluatedNodes;
}

function passedMiddleGround(arena)
{
    for (var i = (parseInt(arena.width) / 2) - 1; i < parseInt(arena.width); i++)
    {
        for(var j = 0; j < parseInt(arena.height); j++)
        {
            if (arena.get(i, j).symbol == "o")
                return true;
        }
    }
    return false;
}

function decide(arena, cell, outputCallback) {

    var nearbyEmpties = arena.getAdjacentMatches(cell.point, [".", "c"]);
    var nearbyEnemies = arena.getAdjacentMatches(cell.point, ["x"]);
    var nearbyCorpses = arena.getAdjacentMatches(cell.point, ["c"]);

    if (nearbyEnemies.length > 4 && cell.energy >= cell.hp && cell.hp <= 3) {
        outputCallback("EXPLODE");
        return;
    }

    //attack whenever we get the chance. leave the replication to the cells doing nothing
    if (cell.energy > 0 && nearbyEnemies.length > 0){
        outputCallback("ATTACK " + arena.getDirection(cell, nearbyEnemies[(nearbyEnemies.length * Math.random()) | 0]) + " " + Math.min(cell.energy, 3));
        return;
    }

    //if we are close to an enemy, move towards it. let the back line fill the new space
    if (cell.energy > 2) {
        for (var i = 0; i < nearbyEmpties.length; ++i) {
            var space = nearbyEmpties[i];
            if (arena.getAdjacentMatches(space, ["x"]).length) {
                outputCallback("MOVE " + arena.getDirection(cell, space));
                return;
            }
        }
    }

    //yum
    if (nearbyCorpses.length > 0) {
        outputCallback("EAT " + arena.getDirection(cell, nearbyCorpses[(nearbyCorpses.length * Math.random()) | 0]));
        return;
    }

    //until we pass the middle ground, just keep moving into tactical position. afterwards we can start replication
    if (passedMiddleGround(arena) && cell.energy < 5 && nearbyEmpties.length > 0)
    {
        outputCallback("REST");
        return;
    }

    //try to block the opponent cells - interrupt their replication
    //if we have enough energy to move, choose the best spot
    if (nearbyEmpties.length > 0 && ((cell.energy >= 2 && nearbyEnemies.length == 0) || cell.energy >= 5)) {

        var nextMove = null;

        if (nearbyEmpties.length === 1) {
            nextMove = nearbyEmpties[0];
        }
        else {
            var reachableNodes = getAllReachableNodes(arena, cell);

            //select nodes that have an adjacent enemy
            var enemyAdjacentNodes = {};
            var enemyNodesReachable = false;
            for (var node in reachableNodes) {
                if (reachableNodes.hasOwnProperty(node) && reachableNodes[node].adjacentEnemies > 0) {
                    enemyAdjacentNodes[node] = reachableNodes[node];
                    enemyNodesReachable = true;
                }
            }

            if (enemyNodesReachable)
            {
                //if there are any then select the closest one
                var closest = null;
                for (var node in enemyAdjacentNodes) {
                    if(!closest)
                    {
                        closest = enemyAdjacentNodes[node];
                    }
                    else{
                        if(enemyAdjacentNodes[node].GetDistance() < closest.GetDistance())
                        {
                            closest = enemyAdjacentNodes[node];
                        }
                    }

                }

                //select the first move of the nodes path
                //trace the selected node back to the first node to select the first move towards the cell.
                while (closest.prevNode != null && closest.prevNode.prevNode != null)
                {
                    closest = closest.prevNode;
                }
                nextMove = arena.get(closest.x, closest.y);
            }
        }

        //a path to the enemy was found
        if(nextMove)
        {
            //do this until we get half way across the board, then we just replicate
            if (!passedMiddleGround(arena)) {
                if (cell.energy >= 5) {
                    outputCallback("DIVIDE " + arena.getDirection(cell, nextMove));
                    return;
                }

                outputCallback("MOVE " + arena.getDirection(cell, nextMove));
                return;
            }
            else {
                outputCallback("DIVIDE " + arena.getDirection(cell, nextMove));
                return;
            }

        }

    }

    //if theres no path to an enemy available, just divide anywhere
    if (cell.energy >= 5 && nearbyEmpties.length > 0) {
        outputCallback("DIVIDE " + arena.getDirection(cell, nearbyEmpties[(nearbyEmpties.length * Math.random()) | 0]));
        return;
    }

    outputCallback("REST");
    return;
}

var input = "";
// quiet stdin EPIPE errors
process.stdin.on("error", function(err) {
    log("slight error: " + err);
});
process.stdin.on("data", function(data) {
    input += data;
});
process.stdin.on("end", function() {
    if(input == "BEGIN") {
        // output space-separated attributes
        process.stdout.write([MAX_HP, MAX_ENERGY, ACIDITY].join(" "));
        clearLog();
    } else {
        // read in arena and decide on an action
        var arena = new Arena();
        var lines = input.split("\n");
        var dimensions = lines[0].split(" ").map(function(d) { return parseInt(d); });
        arena.width = dimensions[0];
        arena.height = dimensions[1];
        for(var y=1; y<=dimensions[1]; ++y) {
            for(var x=0; x<lines[y].length; ++x) {
                arena.set(x, y-1, lines[y][x]);
            }
        }

        var stats = lines[dimensions[1]+2].split(" ");
        var cell = { x: stats[0], y: stats[1], hp: stats[2], energy: stats[3], point: arena.get(stats[0], stats[1]) };

        // decide on an action and write the action to stdout
        decide(arena, cell, function(output) { process.stdout.write(output); })
    }
});

var Arena = function() {
    this.dict = {};
};
Arena.prototype = {
    // get Point object
    get: function(x,y) {
        if(!this.dict[x+","+y])
            return 'w';
        return this.dict[x+","+y];
    },

    // store Point object
    set: function(x,y,d) {
        this.dict[x+","+y] = new Point(x,y,d);
    },

    // get an array of all Points adjacent to this one whose symbol is contained in matchList
    // if matchList is omitted, return all Points
    getAdjacentMatches: function(point, matchList) {
        var result = [];
        for(var i=-1; i<=1; ++i) {
            for(var j=-1; j<=1; ++j) {
                var inspectedPoint = this.get(point.x+i, point.y+j);
                if(inspectedPoint && 
                   (i!=0 || j!=0) &&
                   (!matchList || matchList.indexOf(inspectedPoint.symbol) != -1)) {
                    result.push(inspectedPoint);
                }
            }
        }
        return result;
    },

    // return the direction from point1 to point2
    getDirection: function(point1, point2) {
        var dx = point2.x - point1.x;
        var dy = point2.y - point1.y;
        dx = Math.abs(dx) / (dx || 1);
        dy = Math.abs(dy) / (dy || 1);

        c2d = { "0,0":"-",
                "0,-1":"N", "0,1":"S", "1,0":"E", "-1,0":"W",
                "-1,-1":"NW", "1,-1":"NE", "1,1":"SE", "-1,1":"SW" };

        return c2d[dx + "," + dy];
    }
}

var Point = function(x,y,d) {
    this.x = x;
    this.y = y;
    this.symbol = d;
}
Point.prototype.toString = function() {
    return "(" + this.x + ", " + this.y + ")";
}

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

Btw นี้ดูเหมือนจะไม่ทำงานตามที่ตั้งใจไว้ถ้ามันของผู้เล่น 2
justhalf

3

ทำเซลล์ง่ายnode.jsๆ ค่ะ ผ่านการทดสอบแล้วว่ามีตัวอย่างโหนดเซลล์และต่อต้าน Kostronor ที่มันโจมตีพวกมัน

ปรับปรุง

ยังค่อนข้างง่ายลองไปหาศัตรูหรือหาร

// used in defining cell spec
var MAX_HP = 4;
var MAX_ENERGY = 8;
var ACIDITY = 0;

function decide(arena, cell, outputCallback) {

    var nearbyEmpties = arena.getAdjacentMatches(cell.point, [".", "c"]);
    var nearbyEnemies = arena.getAdjacentMatches(cell.point, ["x"]);
    var nearbyCorpses = arena.getAdjacentMatches(cell.point, ["c"]);
    var nearbyFriends = arena.getAdjacentMatches(cell.point, ["o"]);

    if (nearbyFriends.length >= 8) {
        outputCallback("REST");
        return;
    }

    if (nearbyFriends.length >= 7 && nearbyEnemies.length < 0 && nearbyCorpses.length > 0 && energy < MAX_ENERGY) {
        outputCallback("EAT " + arena.getDirection(cell, nearbyCorpses[(nearbyCorpses.length*Math.random())|0]));
        return;
    }

    // if you have two or more nearby enemies, explode if possible
    if(nearbyEnemies.length >= 1
        && cell.energy >= cell.hp 
        && cell.hp <= 1 
        && nearbyEnemies.length > nearbyFriends.length) {
        outputCallback("EXPLODE");
        return;
    }

    // if you have two or more nearby enemies, explode if possible
    if(nearbyEnemies.length >= 3 && cell.energy >= cell.hp && nearbyEnemies.length > nearbyFriends.length) {
        outputCallback("EXPLODE");
        return;
    }

    // if you have the energy and space to divide, do it
    if(cell.energy >= 5 && nearbyEmpties.length > 0) {
        var ed = arena.getEnemyDirection(cell);
        if (nearbyEmpties.indexOf(ed) >= 0 && Math.random() < 0.5){
            outputCallback("DIVIDE " + ed);
        } else{
            outputCallback("DIVIDE " + arena.getDirection(cell, nearbyEmpties[(nearbyEmpties.length*Math.random())|0]));
        }
        return;
    }

    // if at least one adjacent enemy, attack if possible
    if(cell.energy > 0 && nearbyEnemies.length > 0) {
        outputCallback("ATTACK " + arena.getDirection(cell, nearbyEnemies[(nearbyEnemies.length*Math.random())|0]) + " " + Math.min(cell.energy, 3));
        return;
    }

    if (Math.random() < 0.5) {
        for(var i=0; i<nearbyEmpties.length; ++i) {
            outputCallback("MOVE " + arena.getEnemyDirection(cell));
            return;
        }
    } 

    if (nearbyEmpties.length > 0 && nearbyEnemies.length <= 6) {
        outputCallback("REST"); // because next turn is divide time
        return;
    }

    // if there's a nearby corpse, eat it if your energy is below max
    if(nearbyCorpses.length > 0) {
        outputCallback("EAT " + arena.getDirection(cell, nearbyCorpses[(nearbyCorpses.length*Math.random())|0]));
        return;
    }

    outputCallback("REST");
    return;
}

var input = "";
// quiet stdin EPIPE errors
process.stdin.on("error", function(err) {
    console.log("slight error: " + err);
});
process.stdin.on("data", function(data) {
    input += data;
});
process.stdin.on("end", function() {
    if(input == "BEGIN") {
        // output space-separated attributes
        process.stdout.write([MAX_HP, MAX_ENERGY, ACIDITY].join(" "));
    } else {
        // read in arena and decide on an action
        var arena = new Arena();
        var lines = input.split("\n");
        var dimensions = lines[0].split(" ").map(function(d) { return parseInt(d); });
        arena.width = dimensions[0];
        arena.height = dimensions[1];
        for(var y=1; y<=dimensions[1]; ++y) {
            for(var x=0; x<lines[y].length; ++x) {
                arena.set(x, y-1, lines[y][x]);
            }
        }

        var stats = lines[dimensions[1]+2].split(" ");
        var cell = { x: stats[0], y: stats[1], hp: stats[2], energy: stats[3], point: arena.get(stats[0], stats[1]) };

        // decide on an action and write the action to stdout
        decide(arena, cell, function(output) { process.stdout.write(output); })
    }
});

var Arena = function() {
    this.dict = {};
};
Arena.prototype = {
    // get Point object
    get: function(x,y) {
        return this.dict[x+","+y];
    },

    // store Point object
    set: function(x,y,d) {
        this.dict[x+","+y] = new Point(x,y,d);
    },

    // get an array of all Points adjacent to this one whose symbol is contained in matchList
    // if matchList is omitted, return all Points
    getAdjacentMatches: function(point, matchList) {
        var result = [];
        for(var i=-1; i<=1; ++i) {
            for(var j=-1; j<=1; ++j) {
                var inspectedPoint = this.get(point.x+i, point.y+j);
                if(inspectedPoint && 
                   (i!=0 || j!=0) &&
                   (!matchList || matchList.indexOf(inspectedPoint.symbol) != -1)) {
                    result.push(inspectedPoint);
                }
            }
        }
        return result;
    },

    // return the direction from point1 to point2
    getDirection: function(point1, point2) {
        var dx = point2.x - point1.x;
        var dy = point2.y - point1.y;
        dx = Math.abs(dx) / (dx || 1);
        dy = Math.abs(dy) / (dy || 1);

        c2d = { "0,0":"-",
                "0,-1":"N", "0,1":"S", "1,0":"E", "-1,0":"W",
                "-1,-1":"NW", "1,-1":"NE", "1,1":"SE", "-1,1":"SW" };

        return c2d[dx + "," + dy];
    },

    getEnemyDirection: function(p) {
        for (var i = 0; i < this.width; i++) {
            for (var j = 0; j < this.height; j++) {
                var found = this.get(i,j);
                if (found != null && found.symbol == "x") {
                    return this.getDirection(p, found);
                }
            }
        }
        return "N"; //should never happen
    }
}

var Point = function(x,y,d) {
    this.x = x;
    this.y = y;
    this.symbol = d;
}
Point.prototype.toString = function() {
    return "(" + this.x + ", " + this.y + ")";
}

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

2

วิวัฒนาการ

สิ่งที่ส่งมานี้มีการพัฒนาและไม่ได้เป็นสิ่งมีชีวิตเซลล์เดี่ยวง่ายๆอีกต่อไป! มันพยายามโจมตี / ระเบิดทุกครั้งที่ทำได้ไม่เช่นนั้นจะแบ่งหรือเคลื่อนที่ไปหาศัตรู การเคลื่อนไหวควรแก้ปัญหาของเซลล์ที่ล้อมรอบด้วยเซลล์ที่เป็นมิตรด้วยพลังงานสูงสุดไม่สามารถทำสิ่งที่มีประโยชน์ได้
หลังจากเคลื่อนไหวจะมีพลังงานเหลือ 3 ครั้งในการโจมตีศัตรูอย่างหนักที่สุด

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;

public class Evolution {
    public static final int MAX_HP = 4;
    public static final int MAX_ENERGY = 8;
    public static final int ACIDITY = 0;

    // given arena state and cell stats, return an action string (e.g., "ATTACK NW 2", "DIVIDE S")
    public static String decide(Arena arena, Point cell, int hp, int energy) {
        ArrayList<Point> nearbyEmpty = arena.getAdjacentMatches(cell, ".");
        ArrayList<Point> nearbyEnemies = arena.getAdjacentMatches(cell, "x");
        ArrayList<Point> nearbyCorpses = arena.getAdjacentMatches(cell, "c");
        ArrayList<Point> nearbyFriends = arena.getAdjacentMatches(cell, "o");

        // more than 1 enemy around => explode if possible and worth it
        if(nearbyEnemies.size() > 1 && energy > hp && hp <= 3 && nearbyEnemies.size() > nearbyFriends.size()) {
            return "EXPLODE";
        }

        // enemies around => always attack with max strength
        if(energy > 0 && nearbyEnemies.size() > 0) {
            int attackStrength = Math.min(energy, 3);
            Point enemy = nearbyEnemies.get(0);
            return "ATTACK " + arena.getDirection(cell, enemy) + " " + attackStrength;
        }       

        // safe spot => divide if possible
        if(energy >= 5 && nearbyEmpty.size() > 0) {
            Point randomEmpty = nearbyEmpty.get((int)Math.floor(nearbyEmpty.size()*Math.random()));
            return "DIVIDE " + arena.getDirection(cell, randomEmpty);
        }

        // nearby corpse and missing energy => eat
        if(nearbyCorpses.size() > 0 && energy < MAX_ENERGY) {
            Point corpse = nearbyCorpses.get(0);
            return "EAT " + arena.getDirection(cell, corpse);
        }

        // move towards enemy => constant flow of attacks
        if(energy == 4) {
            return "MOVE " + arena.getEnemyDirection(cell);
        }

        return "REST";
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br =
            new BufferedReader(new InputStreamReader(System.in));

        String firstLine;

        firstLine = br.readLine();
        if(firstLine.equals("BEGIN")) {
            System.out.println(MAX_HP + " " + MAX_ENERGY + " " + ACIDITY);
        } else {
            String[] dimensions = firstLine.split(" ");
            int width = Integer.parseInt(dimensions[0]);
            int height = Integer.parseInt(dimensions[1]);
            Point[][] arena = new Point[height][];
            String input;
            int lineno = 0;

            while(!(input=br.readLine()).equals("")) {
                String[] charList = input.substring(1).split("");
                arena[lineno] = new Point[width];
                for(int i=0; i<charList.length; ++i) {
                    arena[lineno][i] = new Point(i, lineno, charList[i]);
                }
                lineno++;
            }

            String[] stats = br.readLine().split(" ");
            int x = Integer.parseInt(stats[0]);
            int y = Integer.parseInt(stats[1]);
            int hp = Integer.parseInt(stats[2]);
            int energy = Integer.parseInt(stats[3]);

            Arena arenaObj = new Arena(arena, width, height);
            System.out.print(decide(arenaObj, arenaObj.get(x,y), hp, energy));
        }
    }

    public static class Arena {
        public Point[][] array;
        public HashMap<String, String> c2d;
        public int height;
        public int width;

        public Arena(Point[][] array, int width, int height) {
            this.array = array;
            this.width = width;
            this.height = height;

            this.c2d = new HashMap<String, String>();
            this.c2d.put("0,0", "-");
            this.c2d.put("0,-1", "N");
            this.c2d.put("0,1", "S");
            this.c2d.put("1,0", "E");
            this.c2d.put("-1,0", "W");
            this.c2d.put("-1,-1", "NW");
            this.c2d.put("1,-1", "NE");
            this.c2d.put("-1,1", "SW");
            this.c2d.put("1,1", "SE");
        }

        // get the character at x,y
        // or return empty string if out of bounds
        public Point get(int x, int y) {
            if(y < 0 || y >= this.array.length){
                return null;
            }

            Point[] row = this.array[y];

            if(x < 0 || x >= row.length) {
                return null;
            }

            return row[x];
        }

        // get arraylist of Points for each adjacent space that matches the target string
        public ArrayList<Point> getAdjacentMatches(Point p, String match) {
            ArrayList<Point> result = new ArrayList<Point>();
            for(int i=-1; i<=1; ++i) {
                for(int j=-1; j<=1; ++j) {
                    Point found = this.get(p.x+i, p.y+j);
                    if((i!=0 || j!=0) && found != null && found.symbol.equals(match)) {
                        result.add(found);
                    }
                }
            }
            return result;
        }

        // get the direction string from point 1 to point 2
        public String getDirection(Point p1, Point p2) {
            int dx = p2.x - p1.x;
            int dy = p2.y - p1.y;
            dx = Math.abs(dx) / (dx==0?1:dx);
            dy = Math.abs(dy) / (dy==0?1:dy);

            return this.c2d.get(dx + "," + dy);
        }

        public String getEnemyDirection(Point p) {
            for (int x = 0; x < width; x++) {
                for (int y = 0; y < height; y++) {
                    Point found = this.get(x,y);
                    if (found != null && found.symbol.equals("x")) {
                        return getDirection(p, found);
                    }
                }
            }
            return "N"; //should never happen
        }
    }

    public static class Point {
        int x, y;
        String symbol;

        public Point(int x, int y, String sym) {
            this.x=x;
            this.y=y;
            this.symbol=sym;
        }
    }
}

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

2

บ้าบิ่น

เพราะฉันใช้ Clojure ซึ่งมีข้อ จำกัด บางอย่างโดยเฉพาะอย่างยิ่งเวลาเริ่มต้นที่ยิ่งใหญ่ฉันจึงใช้ตับเล็กน้อย เมื่อโปรแกรมได้รับBEGINมัน4 6 2 LOOPแสดงว่ามันไม่หยุด ENDจากนั้นก็จะใช้เวลาปัจจัยการผลิตเป็นกระแสอย่างต่อเนื่องและจบลงด้วย มันไม่ได้บันทึกสถานะใด ๆ ซึ่งจะทำให้ชัดเจนโดยไม่ใช้ตัวแปรโกลบอลใด ๆ หรือการนำค่ากลับมาใช้ซ้ำ เนื่องจากยังไม่มีการใช้งานสำหรับการกระทำแบบวนรอบฉันจึงไม่สามารถทดสอบโค้ดได้อย่างสมบูรณ์ (ฉันหวังว่ารหัสจะชัดเจนเพียงพอ)

เซลล์ได้รับชื่อมาจากลักษณะของการระเบิดทุกครั้งที่ทำได้ (และมีความเป็นกรด) และจัดลำดับความสำคัญของการโจมตีทันทีหลังจากหาร

ฉันได้อัปโหลดที่สร้างโถไฟล์ของฉันDropbox ทำงานด้วยjava -jar petridish-clojure.jar

เพียงชี้แจง:

> BEGIN
< 4 6 2 LOOP
> 10 4
> ..........
> ..xx.c....
> ...c...O..
> ......o...
> 
> 3 4 6
< DIVIDE NW
> 10 4
> ..........
> ..xx.c....
> ...c.o.o..
> ......o...
>
> 5 2 4 1
< EAT N
> END
(ns petridish.core
  (:require [clojure.string :as s])
  (:gen-class))

(def ^:const maxhp     4)
(def ^:const maxenergy 6)
(def ^:const acidity   2)

(defn str->int
  [x]
  (if (empty? x)
    0
    (Integer. (re-find #"\d+" x))))

(defn sum-vectors [vec1 vec2]
  (vec (map #(vec (map + % vec2)) vec1)))

(defn find-adjacent [[width height] board pos target]
  (let [cells (sum-vectors [[-1 -1] [0 -1] [1 -1]
                            [-1  0]        [1  0]
                            [-1  1] [0  1] [1  1]]
                           pos)
        directions ["NW" "N" "NE"
                    "W"      "E"
                    "SW" "S" "SE"]]
    (for [cell cells
          :when (and (> width  (cell 0) -1)
                     (> height (cell 1) -1)
                     (= target (get-in board (reverse cell))))]
      (directions (.indexOf cells cell)))))

(defn decide [size board [x y hp energy]]
  (let [friends (find-adjacent size board [x y] \o)
        enemies (find-adjacent size board [x y] \x)
        corpses (find-adjacent size board [x y] \c)
        empty   (find-adjacent size board [x y] \.)]
    (cond
      (and (<= hp 3) (> energy hp) (seq enemies))
        "EXPLODE"
      (and (>= energy 5) (seq empty))
        (str "DIVIDE " (first empty))
      (and (>= energy 3) (seq enemies))
        (str "ATTACK " (first enemies) " " (min 3 energy))
      (and (< energy maxenergy) (seq corpses))
        (str "EAT " (first corpses))
      (or (and (<= 5 energy maxenergy) (not (seq empty))) (< energy 5))
        "REST"
      (seq empty)
        (str "MOVE " (rand-nth empty)))))

(defn read-board [[width height]]
  (let [result (vec (for [i (range height)]
                        (read-line)))]
    (read-line) ; Skip the empty line
    result))

(defn reader []
  (loop []
    (let [firstline (read-line)]
      (when (not= firstline "END")
        (println
          (if (= firstline "BEGIN")
            (str maxhp " " maxenergy " " acidity " LOOP")
            (let [size   (map str->int (s/split firstline #"\s+"))
                  board  (read-board size)
                  status (map str->int (s/split (read-line) #"\s+"))]
              (decide size board status))))
        (recur)))))

(defn -main []
  (reader))

อัปเดตบันทึก

1. Fixed the logic a little and removed redundancies.

การใช้ความเป็นกรดดีจริงๆแล้วฉันคิดว่านี่เป็นบอทเดียวที่ใช้ความเป็นกรดเลย
Alex

@Alex เราจะเห็นว่ามันเป็นอย่างไร แต่ฉันคิดว่านี่น่าจะสามารถกำจัด Amoeba ได้ คุณคิดยังไงกับรหัส? ฉันใหม่ที่จะปิดบัง
seequ

ในตัวอย่างของคุณเซลล์ที่เพิ่งเกิดใหม่จะย้ายได้อย่างไร ฉันคิดว่าคุณต้องรออีกหนึ่งรอบ?
justhalf

@justhalf เอ๊ะเซลล์ไม่ทราบว่าพวกเขาอายุเท่าไหร่
seequ

ใช่ แต่คอนโทรลเลอร์รู้ใช่ไหม ไม่ควรหันไปหาเซลล์ที่เพิ่งสร้างใหม่
justhalf

2

บอทหิว

นี่คือข้อความใน R. ฉันหวังว่าฉันเข้าใจถูกต้องว่าข้อมูลจำเพาะทางเทคนิคเกี่ยวกับวิธีการสื่อสารกับโปรแกรมของคุณคืออะไร Rscript Hungryhungrybot.Rควรจะมีการหารือกับ
ถ้ามันมีพลังงานอย่างน้อย 6 ตัวมันก็จะแบ่งออกไป มิฉะนั้นมันจะกินอะไรก็ตามที่อยู่ถัดจากมันหรืออะไรก็ตามที่เข้าถึงได้ หากไม่มีอาหารสามารถเข้าถึงได้มันจะระเบิดเมื่อมีศัตรูรอบ ๆ มากกว่าเซลล์น้องสาวหรือต่อสู้กับศัตรูที่อยู่ใกล้เคียง พักต่อเมื่อพลังงานเป็น 0 และไม่มีอาหารให้กิน

infile <- file("stdin")
open(infile)
input1 <- readLines(infile,1)
if(input1=="BEGIN"){
    out <- "4 7 1"
    }else{
        nr <- as.integer(strsplit(input1," ")[[1]][2])
        nc <- as.integer(strsplit(input1," ")[[1]][1])
        input2 <- readLines(infile, 2+as.integer(nr))
        arena <- do.call(rbind,strsplit(input2[1:nr],""))
        stats <- strsplit(input2[nr+2]," ")[[1]]
        coords <- as.integer(stats[2:1])+1
        hp <- as.integer(stats[3])
        nrj <- as.integer(stats[4])
        closest <- function(coords,arena,object){
            a <- which(arena==object,arr.ind=TRUE)
            if(length(a)){
                d <- apply(a,1,function(x)max(abs(x-coords)))
                b <- which.min(d)
                f <- a[b,]
                dir <- f-coords
                where <- ""
                if(dir[1]<0)where <- paste(where,"N",sep="")
                if(dir[1]>0)where <- paste(where,"S",sep="")
                if(dir[2]<0)where <- paste(where,"W",sep="")
                if(dir[2]>0)where <- paste(where,"E",sep="")
                dist <- d[b]
                }else{dist <- NA; where <- ""}
            list(dist,where)
            }
        near <- expand.grid((coords[1]-1):(coords[1]+1),(coords[2]-1):(coords[2]+1))
        near <- near[near[,1]<=nr&near[,2]<=nc,]
        adjacent <- t(matrix(apply(near,1,function(x)arena[x[1],x[2]]),nr=3,byrow=TRUE))
        w <- matrix(c('NW','N','NE','W','','E','SW','S','SE'),nr=3,byrow=TRUE)
        if(coords[1]==1) w <- w[-1,]
        if(coords[1]==nr) w <- w[-3,]
        if(coords[2]==1) w <- w[,-1]
        if(coords[2]==nc) w <- w[,-3]
        if(any(arena=="c")){food <- closest(coords,arena,"c")}else{food <- list(nrj+2,"")}
        enemies <- closest(coords,arena,"x")
        if(nrj>=6){
            empties <- w[adjacent=="."]
            if(!length(empties)){
                if(sum(adjacent=="x")>sum(adjacent=="o") & hp<=3 & nrj>=hp){
                    out <- "EXPLODE"
                    }else{out <- "REST"}
                }else if(enemies[[2]]%in%empties & enemies[[1]]!=1){
                out <- paste("DIVIDE", enemies[[2]])
                }else{
                out <- paste("DIVIDE", empties[1])
                }
            }else{
                if(nrj==0 & !any(adjacent=="c")){
                    out <- "REST"
                    }else{
                        if(any(adjacent=="c")){
                            out <- paste("EAT",w[adjacent=="c"][1])
                            }else if(any(arena=="c") & food[[1]]<=(nrj+1)){
                                    out <- paste("MOVE",food[[2]])
                            }else if(sum(adjacent=="x")>sum(adjacent=="o") & hp<=3 & nrj>=hp){
                                out <- "EXPLODE"
                            }else if(any(adjacent=="x")){
                                out <- paste("ATTACK",w[adjacent=="x"][1],max(nrj,3))
                            }else{
                                out <- paste("MOVE", enemies[[2]])
                            }
                    }
            }
        }
cat(out)
flush(stdout())

ฉัน (ในที่สุด) พยายามที่จะเรียกใช้ความท้าทายและฉันได้รับError: unexpected 'else' in "else"ในรหัสของคุณ ฉันกลัวว่าฉันจะไม่รู้จัก R เลยดังนั้นฉันจึงไม่สามารถแก้ไขข้อผิดพลาดนี้ได้ BEGINสำหรับการอ้างอิงของฉันได้รับข้อผิดพลาดนี้ทั้งเมื่อฉันทำงานในคนขับรถและเมื่อฉันเพียงแค่เรียกใช้โปรแกรมและประเภทด้วยตนเอง
apsillers

@apsillers arf ฉันเพิ่มบรรทัดใหม่ที่ฉันไม่ควรมี: มันควรจะทำงานแล้ว
plannapus

แก้ไขข้อผิดพลาดนั้นเพื่อให้เราสามารถผ่านการเริ่มต้นของเซลล์ ตอนนี้ฉันได้เกมใหม่เมื่อเกมเริ่ม:Error in if (dir[1] < 0) where <- paste(where, "N", sep = "") : missing value where TRUE/FALSE needed
apsillers

ตอนนี้เทิร์นแรกวิ่งได้ดี แต่ผลเทิร์นต่อมาจะผลิตออกมาError: object 'food' not found(เมื่อเผชิญหน้ากับการยอมแพ้ของอเล็กซ์รู
เบิล

เซลล์ของคุณตอนนี้ทำงานได้ดีขอบคุณ! :) อย่างไรก็ตาม justhalf ได้ระบุข้อผิดพลาดร้ายแรงบางประการในโปรแกรมไดรเวอร์ (MOVE นั้นไม่มีค่าใช้จ่ายและ EXPLODE ไม่ได้คำนึงถึงความเป็นกรด) หากคุณสนใจที่จะทดสอบซ้ำกับรหัสไดรเวอร์ที่อัปเดตและอัปเดตการส่งของคุณโปรดแจ้งให้เราทราบ ถ้าไม่อย่างนั้นก็ดี
apsillers

2

แบคทีเรียประสานงาน

ฉันหวังว่าฉันจะไม่สายเกินไป

ชนะกับคู่ต่อสู้คนอื่น ๆ (และมักจะฆ่าพวกเขาทั้งหมด) ในการทดสอบของฉันและการต่อสู้จะไม่จบลงถ้ามันเผชิญหน้ากับตัวเองหลักฐานที่แสดงว่ากลยุทธ์นั้นแข็งแกร่ง

เมื่อคุณได้รับเซลล์เดียวคุณสามารถจดจำสถานะก่อนหน้านี้ แต่คุณสามารถใช้ตำแหน่งของตัวเองเพื่อทำงานที่แตกต่าง! =)

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

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

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

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

เรียกใช้ด้วย java CoordinatedBacteria

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;

public class CoordinatedBacteria {
    public static final int MAX_HP = 6;
    public static final int MAX_ENERGY = 6;
    public static final int ACIDITY = 0;

    // given arena state and cell stats, return an action string (e.g., "ATTACK NW 2", "DIVIDE S")
    public static String decide(final Arena arena, Point cell, int hp, int energy) {
        // empty and corpses are free for movement and division
        final Point2D enemyCenter = arena.getCenterOf("x");
        final Point2D ourCenter = arena.getCenterOf("o");
        final int moverPos = (enemyCenter.x <= ourCenter.x || enemyCenter.y <= ourCenter.y) ? (arena.width+arena.height+1)%2 : 1;
        final int attackPos = (enemyCenter.x <= ourCenter.x || enemyCenter.y <= ourCenter.y) ? (arena.width+arena.height)%2 : 1;

        int selfCount = arena.count("o");
        boolean isMidWay = selfCount > (arena.width*arena.height/2-1);

        if(!isMidWay){
            if(enemyCenter.x < ourCenter.x){
                enemyCenter.x = 0;
                enemyCenter.y = 0;
                ourCenter.x = arena.width;
                ourCenter.y = arena.height;
            } else {
                enemyCenter.x = arena.width;
                enemyCenter.y = arena.height;
                ourCenter.x = 0;
                ourCenter.y = 0;
            }
        }
        ArrayList<Point> nearbyEmpty = arena.getAdjacentMatches(cell, ".");
        Collections.sort(nearbyEmpty, new Comparator<Point>(){
            @Override
            public int compare(Point o1, Point o2) {
                Double score1 = arena.getAdjacentMatches(o1, ".").size()
                        + arena.getAdjacentMatches(o1, "c").size()
                        + arena.getAdjacentMatches(o1, "x").size()
                        - arena.getAdjacentMatches(o1, "o").size()
                        + distance(o1.x, o1.y, enemyCenter.x, enemyCenter.y)*100;
                Double score2 = arena.getAdjacentMatches(o2, ".").size()
                        + arena.getAdjacentMatches(o2, "c").size()
                        + arena.getAdjacentMatches(o2, "x").size()
                        - arena.getAdjacentMatches(o2, "o").size()
                        + distance(o1.x, o1.y, enemyCenter.x, enemyCenter.y)*100;
                return Double.compare(score2, score1);
            }
        });
        ArrayList<Point> nearbyEnemies = arena.getAdjacentMatches(cell, "x");
        Collections.sort(nearbyEnemies, new Comparator<Point>(){
            @Override
            public int compare(Point o1, Point o2) {
                Integer score1 = (arena.getAdjacentMatches(o1, ".").size()
                        + arena.getAdjacentMatches(o1, "c").size()
                        - arena.getAdjacentMatches(o1, "x").size()
                        + arena.getAdjacentMatches(o1, "o").size())
                        *10
                        + (isAtBoundary(o1, arena)?1000:0)
                        + (o1.x + o1.y + attackPos + 1)%2;
                Integer score2 = (arena.getAdjacentMatches(o2, ".").size()
                        + arena.getAdjacentMatches(o2, "c").size()
                        - arena.getAdjacentMatches(o2, "x").size()
                        + arena.getAdjacentMatches(o2, "o").size())
                        *10
                        + (isAtBoundary(o2, arena)?1000:0)
                        + (o2.x + o2.y + attackPos + 1)%2;
                return Integer.compare(score2, score1);
            }
        });
        ArrayList<Point> nearbyCorpses = arena.getAdjacentMatches(cell, "c");
        Collections.sort(nearbyCorpses, new Comparator<Point>(){
            @Override
            public int compare(Point o1, Point o2) {
                Integer score1 = arena.getAdjacentMatches(o1, "x").size()
                        - arena.getAdjacentMatches(o1, "o").size();
                Integer score2 = arena.getAdjacentMatches(o2, "x").size()
                        - arena.getAdjacentMatches(o2, "o").size();
                return Integer.compare(score1, score2);
            }
        });
        ArrayList<Point> nearbyFriends = arena.getAdjacentMatches(cell, "o");

        for(Point empty: nearbyEmpty){
            if(nearbyFriends.size()>=2 && energy >= 1 && arena.getAdjacentMatches(empty, "x").size()==3 && isAtBoundary(empty, arena)){
                return "MOVE "+arena.getDirection(cell, empty);
            }
        }

        for(Point empty: nearbyCorpses){
            if(nearbyFriends.size()>=2 && energy >= 1 && arena.getAdjacentMatches(empty, "x").size()==3 && isAtBoundary(empty, arena)){
                return "MOVE "+arena.getDirection(cell, empty);
            }
        }

        if ((cell.x+cell.y)%2 == moverPos && energy >= 1 && energy <= 5){
            if(nearbyEmpty.size()>0){
                Point foremost = nearbyEmpty.get(0);
                if(nearbyFriends.size() >= 4){
                    return "MOVE "+arena.getDirection(cell, foremost);
                }
            }
            if(nearbyCorpses.size() > 0) {
                Point corpse = nearbyCorpses.get(0);
                return "EAT " + arena.getDirection(cell, corpse);
            }

            if(energy > 0 && nearbyEnemies.size() > 0) {
                int attackStrength = Math.min(energy, 3);
                Point enemy = nearbyEnemies.get(0);
                return "ATTACK " + arena.getDirection(cell, enemy) + " " + attackStrength;
            }

            if(nearbyFriends.size() >= 4 && nearbyEmpty.size() > 0){
                Point movePoint = getBestPointToDivide(arena, nearbyEmpty);
                return "MOVE " + arena.getDirection(cell, movePoint);
            }
        }

        if(energy >= 5 && nearbyEmpty.size() > 0) {
            Point divisionPoint = getBestPointToDivide(arena, nearbyEmpty);
            if(energy == MAX_ENERGY && nearbyFriends.size() >= 5
                    && distance(enemyCenter.x, enemyCenter.y, cell.x, cell.y) > distance(enemyCenter.x, enemyCenter.y, divisionPoint.x, divisionPoint.y)){
                return "MOVE " + arena.getDirection(cell, divisionPoint);
            }
            return "DIVIDE " + arena.getDirection(cell, divisionPoint);
        }

        if(nearbyCorpses.size() > 0) {
            Point corpse = nearbyCorpses.get(0);
            if (energy < MAX_ENERGY){
                return "EAT " + arena.getDirection(cell, corpse);
            } else {
                return "DIVIDE " + arena.getDirection(cell, corpse);
            }
        }

        if(energy >= 5 && nearbyCorpses.size() > 0) {
            Point divisionPoint = getBestPointToDivide(arena, nearbyCorpses);
            if(energy == MAX_ENERGY && nearbyFriends.size() >= 5
                    && distance(enemyCenter.x, enemyCenter.y, cell.x, cell.y) < distance(enemyCenter.x, enemyCenter.y, divisionPoint.x, divisionPoint.y)){
                return "MOVE " + arena.getDirection(cell, divisionPoint);
            }
            return "DIVIDE " + arena.getDirection(cell, divisionPoint);
        }

        // if at least one adjacent enemy, attack if possible
        if(energy > 0 && nearbyEnemies.size() > 0) {
            int attackStrength = Math.min(energy, 3);
            Point enemy = nearbyEnemies.get(0);
            return "ATTACK " + arena.getDirection(cell, enemy) + " " + attackStrength;
        }

        return "REST";

    }

    public static boolean isAtBoundary(Point point, Arena arena){
        return point.x==0 || point.x==arena.width-1 || point.y==0 || point.y==arena.height-1;
    }

    public static double distance(double x1, double y1, double x2, double y2){
        return (x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);
    }

    public static Point getBestPointToDivide(Arena arena, List<Point> nearbyEmpty){
        Point result = null;
        double minDist = 100000;
        List<Point> mostEmpty = new ArrayList<Point>();
        int max = -1000;
        List<Point> neighbor = nearbyEmpty;
        for(Point point: neighbor){
            int emptyNeighborScore = arena.getAdjacentMatches(point, ".").size()
                    + arena.getAdjacentMatches(point, "c").size()
                    + arena.getAdjacentMatches(point, "x").size()
                    - arena.getAdjacentMatches(point, "o").size();
            if(emptyNeighborScore > max){
                mostEmpty = new ArrayList<Point>();
                mostEmpty.add(point);
                max = emptyNeighborScore;
            } else if(emptyNeighborScore == max){
                mostEmpty.add(point);
            }
        }
        for(Point point: mostEmpty){
            Point2D enemyCenter = arena.getCenterOf("x");
            double dist = Math.pow(point.x-enemyCenter.x, 2) + Math.pow(point.y-enemyCenter.y, 2);
            if(dist < minDist){
                minDist = dist;
                result = point;
            }
        }
        return result;
    }

    public static void main(String[] args) throws IOException {
        BufferedReader br =
                new BufferedReader(new InputStreamReader(System.in));

        String firstLine;

        firstLine = br.readLine();
        if(firstLine.equals("BEGIN")) {
            System.out.println(MAX_HP + " " + MAX_ENERGY + " " + ACIDITY);
        } else {
            String[] dimensions = firstLine.split(" ");
            int width = Integer.parseInt(dimensions[0]);
            int height = Integer.parseInt(dimensions[1]);
            Point[][] arena = new Point[height][];
            String input;
            int lineno = 0;

            while(!(input=br.readLine()).equals("")) {
                char[] charList = input.toCharArray();
                arena[lineno] = new Point[width];
                for(int i=0; i<charList.length; ++i) {
                    arena[lineno][i] = new Point(i, lineno, charList[i]);
                }
                lineno++;
            }

            String[] stats = br.readLine().split(" ");
            int x = Integer.parseInt(stats[0]);
            int y = Integer.parseInt(stats[1]);
            int hp = Integer.parseInt(stats[2]);
            int energy = Integer.parseInt(stats[3]);

            Arena arenaObj = new Arena(arena, width, height);
            System.out.print(decide(arenaObj, arenaObj.get(x,y), hp, energy));
        }
    }

    public static class Arena {
        public Point[][] array;
        public HashMap<String, String> c2d;
        public int height;
        public int width;

        public Arena(Point[][] array, int width, int height) {
            this.array = array;
            this.width = width;
            this.height = height;


            this.c2d = new HashMap<String, String>();
            this.c2d.put("0,0", "-");
            this.c2d.put("0,-1", "N");
            this.c2d.put("0,1", "S");
            this.c2d.put("1,0", "E");
            this.c2d.put("-1,0", "W");
            this.c2d.put("-1,-1", "NW");
            this.c2d.put("1,-1", "NE");
            this.c2d.put("-1,1", "SW");
            this.c2d.put("1,1", "SE");
        }

        // get the character at x,y
        // or return empty string if out of bounds
        public Point get(int x, int y) {
            if(y < 0 || y >= this.array.length){
                return null;
            }

            Point[] row = this.array[y];

            if(x < 0 || x >= row.length) {
                return null;
            }

            return row[x];
        }

        // get arraylist of Points for each adjacent space that matches the target string
        public ArrayList<Point> getAdjacentMatches(Point p, String match) {
            ArrayList<Point> result = new ArrayList<Point>();
            for(int i=-1; i<=1; ++i) {
                for(int j=-1; j<=1; ++j) {
                    Point found = this.get(p.x+i, p.y+j);
                    if((i!=0 || j!=0) && found != null && found.symbol.equals(match)) {
                        result.add(found);
                    }
                }
            }
            return result;
        }

        public ArrayList<Point> getAdjacents(Point p){
            ArrayList<Point> result = new ArrayList<Point>();
            for(int i=-1; i<=1; ++i) {
                for(int j=-1; j<=1; ++j) {
                    Point found = this.get(p.x+i, p.y+j);
                    if((i!=0 || j!=0) && found != null) {
                        result.add(found);
                    }
                }
            }
            return result;
        }

        public int count(String sym){
            int result = 0;
            for(int y=0; y<array.length; y++){
                for(int x=0; x<array[y].length; x++){
                    Point cur = this.get(x, y);
                    if(cur!=null && cur.symbol.equals(sym)){
                        result++;
                    }
                }
            }
            return result;
        }

        // get the direction string from point 1 to point 2
        public String getDirection(Point p1, Point p2) {
            int dx = p2.x - p1.x;
            int dy = p2.y - p1.y;
            dx = Math.abs(dx) / (dx==0?1:dx);
            dy = Math.abs(dy) / (dy==0?1:dy);

            return this.c2d.get(dx + "," + dy);
        }

        public Point2D getCenterOf(String sym){
            Point2D result = new Point2D(0,0);
            int count = 0;
            for(int y=0; y<array.length; y++){
                for(int x=0; x<array[y].length; x++){
                    if(this.get(x,y).symbol.equals(sym)){
                        result.x += x;
                        result.y += y;
                        count++;
                    }
                }
            }
            result.x /= count;
            result.y /= count;
            return result;
        }

    }

    public static class Point {
        int x, y;
        String symbol;

        public Point(int x, int y, String sym) {
            this.x=x;
            this.y=y;
            this.symbol=sym;
        }

        public Point(int x, int y, char sym){
            this(x, y, ""+sym);
        }
    }

    public static class Point2D{
        double x,y;
        public Point2D(double x, double y){
            this.x = x;
            this.y = y;
        }
    }
}

1

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

มีปัญหาในตรรกะของคุณที่ eat-action จะออก ATTACK แทน EAT และจะทำให้ศพเสีย

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

ดูเหมือนว่ากรดจะเป็นจุดเสียสำหรับฉันดังนั้นฉันจึงเก็บมันไว้ ...

ฉันไม่ได้ทดสอบการส่งเนื่องจากเป็นเวลา 2 นาที;)

นี่คือรหัสของฉัน:

/*
 Sample code for a "Battle for the Petri Dish" cell

 Released under the terms of the WTF Public License
 No warranty express or implied is granted, etc, etc.

 I just hacked this together very quickly; improvements are welcome, so please fork the Gist if you like.

 used this code for a submission @kostronor

 */

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;

public class SlimeCell {
    public static final int MAX_HP = 4;
    public static final int MAX_ENERGY = 8;
    public static final int ACIDITY = 0;

    // given arena state and cell stats, return an action string (e.g., "ATTACK NW 2", "DIVIDE S")
    public static String decide(final Arena arena, final Point cell, final int hp, final int energy) {
        // empty and corpses are free for movement and division
        ArrayList<Point> nearbyEmpty = arena.getAdjacentMatches(cell, ".");
        nearbyEmpty.addAll(arena.getAdjacentMatches(cell, "c"));

        ArrayList<Point> nearbyEnemies = arena.getAdjacentMatches(cell, "x");
        ArrayList<Point> nearbyCorpses = arena.getAdjacentMatches(cell, "c");
        ArrayList<Point> nearbyFriends = arena.getAdjacentMatches(cell, "o");

        // if you have energy and space to divide, divide into a random space
        if((energy >= 5) && (nearbyEmpty.size() > 0)) {
            Point randomEmpty = nearbyEmpty.get((int)Math.floor(nearbyEmpty.size()*Math.random()));
            return "DIVIDE " + arena.getDirection(cell, randomEmpty);
        }

        // if at least one adjacent enemy, attack if possible
        if((energy > 0) && (nearbyEnemies.size() > 1)) {
            int attackStrength = Math.min(energy, 3);
            Point enemy = nearbyEnemies.get((int)Math.floor(nearbyEnemies.size()*Math.random()));
            return "ATTACK " + arena.getDirection(cell, enemy) + " " + attackStrength;
        }

        // if there's a nearby corpse, eat it if your energy is below max
        if(nearbyCorpses.size() > 0) {
            Point corpse = nearbyCorpses.get((int)Math.floor(nearbyCorpses.size()*Math.random()));
            return "EAT " + arena.getDirection(cell, corpse);
        }

        return "REST";

    }

    public static void main(final String[] args) throws IOException {
        BufferedReader br =
                new BufferedReader(new InputStreamReader(System.in));

        String firstLine;

        firstLine = br.readLine();
        if(firstLine.equals("BEGIN")) {
            System.out.println(MAX_HP + " " + MAX_ENERGY + " " + ACIDITY);
        } else {
            String[] dimensions = firstLine.split(" ");
            int width = Integer.parseInt(dimensions[0]);
            int height = Integer.parseInt(dimensions[1]);
            Point[][] arena = new Point[height][];
            String input;
            int lineno = 0;

            while(!(input=br.readLine()).equals("")) {
                String[] charList = input.substring(1).split("");
                arena[lineno] = new Point[width];
                for(int i=0; i<charList.length; ++i) {
                    arena[lineno][i] = new Point(i, lineno, charList[i]);
                }
                lineno++;
            }

            String[] stats = br.readLine().split(" ");
            int x = Integer.parseInt(stats[0]);
            int y = Integer.parseInt(stats[1]);
            int hp = Integer.parseInt(stats[2]);
            int energy = Integer.parseInt(stats[3]);

            Arena arenaObj = new Arena(arena, width, height);
            System.out.print(decide(arenaObj, arenaObj.get(x,y), hp, energy));
        }
    }

    public static class Arena {
        public Point[][] array;
        public HashMap<String, String> c2d;
        public int height;
        public int width;

        public Arena(final Point[][] array, final int width, final int height) {
            this.array = array;
            this.width = width;
            this.height = height;

            this.c2d = new HashMap<String, String>();
            this.c2d.put("0,0", "-");
            this.c2d.put("0,-1", "N");
            this.c2d.put("0,1", "S");
            this.c2d.put("1,0", "E");
            this.c2d.put("-1,0", "W");
            this.c2d.put("-1,-1", "NW");
            this.c2d.put("1,-1", "NE");
            this.c2d.put("-1,1", "SW");
            this.c2d.put("1,1", "SE");
        }

        // get the character at x,y
        // or return empty string if out of bounds
        public Point get(final int x, final int y) {
            if((y < 0) || (y >= this.array.length)){
                return null;
            }

            Point[] row = this.array[y];

            if((x < 0) || (x >= row.length)) {
                return null;
            }

            return row[x];
        }

        // get arraylist of Points for each adjacent space that matches the target string
        public ArrayList<Point> getAdjacentMatches(final Point p, final String match) {
            ArrayList<Point> result = new ArrayList<Point>();
            for(int i=-1; i<=1; ++i) {
                for(int j=-1; j<=1; ++j) {
                    Point found = this.get(p.x+i, p.y+j);
                    if(((i!=0) || (j!=0)) && (found != null) && found.symbol.equals(match)) {
                        result.add(found);
                    }
                }
            }
            return result;
        }

        // get the direction string from point 1 to point 2
        public String getDirection(final Point p1, final Point p2) {
            int dx = p2.x - p1.x;
            int dy = p2.y - p1.y;
            dx = Math.abs(dx) / (dx==0?1:dx);
            dy = Math.abs(dy) / (dy==0?1:dy);

            return this.c2d.get(dx + "," + dy);
        }

    }

    public static class Point {
        int x, y;
        String symbol;

        public Point(final int x, final int y, final String sym) {
            this.x=x;
            this.y=y;
            this.symbol=sym;
        }
    }
}

1

เครื่องบินทิ้งระเบิดกระจายบาง ๆ

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

มันเป็นกลยุทธ์ที่น่ารักและน่าจะเล่นได้ไม่ดี แต่ฉันอยากรู้ว่ามันเป็นอย่างไร ฉันจะทดสอบและปรับปรุงในภายหลังวันนี้อาจจะ

/*
 Sample code for a "Battle for the Petri Dish" cell

 Released under the terms of the WTF Public License,
 No warranty express or implied is granted, etc, etc.

 I just hacked this together very quickly; improvements are welcome, so please fork the Gist if you like.
*/

// used in defining cell spec
var MAX_HP = 1;
var MAX_ENERGY = 7;
var ACIDITY = 4;

/*
   The decide function takes an Arena object (see below for prototype methods), a cell object,
   and an outputCallback, which accepts a command string to output
*/
function decide(arena, cell, outputCallback) {
    var nearbyEmpties = arena.getAdjacentMatches(cell.point, [".", "c"]);
    var nearbyEnemies = arena.getAdjacentMatches(cell.point, ["x"]);
    var nearbyCorpses = arena.getAdjacentMatches(cell.point, ["c"]);
    var nearbyFriendlies = arena.getAdjacentMatches(cell.point, ["o"]);

    //attempt to move away from friendlies if possible
    if(nearbyFriendlies.length>1 && cell.energy>0)
    {
        for(var i=0; i<nearbyEmpties.length; ++i)
        {
            var space = nearbyEmpties[i];
            if(arena.getAdjacentMatches(space, ["o"]).length == 1)
            {
                outputCallback("MOVE " + arena.getDirection(cell,space));
                return;
            }
        }
    }

    // Explode if there are two more adjacent enemies than friendlies or enemies and no friendlies.
    if((nearbyEnemies.length - nearbyFriendlies.length > 1 || (nearbyEnemies.length>0 && nearbyFriendlies.length == 0)) 
        && cell.energy >= cell.hp && cell.hp <= 3)
    {
        outputCallback("EXPLODE");
        return;
    }

    // if you have the energy and space to divide, and there's a way for the child to get away from friendlies, do it.
    if(cell.energy >= 5 && nearbyEmpties.length > 0)
    {
        for(var i=0; i<nearbyEmpties.length; ++i)
        {
            var space = nearbyEmpties[i];
            var possiblePositions = arena.getAdjacentMatches(space, ["o"]);
            for(var i=0; i<possiblePositions.length; ++i)
            {
                if(arena.getAdjacentMatches(possiblePositions[i], ["o"]).length == 0)
                {
                    outputCallback("DIVIDE " + arena.getDirection(cell,space));
                    return;
                }
            }
        }
    }

    // if at least one adjacent enemy, attack if possible
    if(cell.energy > 0 && nearbyEnemies.length > 0)
    {
        outputCallback("ATTACK " + arena.getDirection(cell, nearbyEnemies[(nearbyEnemies.length*Math.random())|0]) + " " + Math.min(cell.energy, 3));
        return;
    }

    // if there's a nearby corpse, eat it if your energy is below max
    if(nearbyCorpses.length > 0)
    {
        outputCallback("EAT " + arena.getDirection(cell, nearbyCorpses[(nearbyCorpses.length*Math.random())|0]));
        return;
    }

    outputCallback("REST");
    return;
}

var input = "";
// quiet stdin EPIPE errors
process.stdin.on("error", function(err) {
    //console.log("slight error: " + err);
});
process.stdin.on("data", function(data) {
    input += data;
});
process.stdin.on("end", function() {
    if(input == "BEGIN") {
        // output space-separated attributes
        process.stdout.write([MAX_HP, MAX_ENERGY, ACIDITY].join(" "));
    } else {
        // read in arena and decide on an action
        var arena = new Arena();
        var lines = input.split("\n");
        var dimensions = lines[0].split(" ").map(function(d) { return parseInt(d); });
        arena.width = dimensions[0];
        arena.height = dimensions[1];
        for(var y=1; y<=dimensions[1]; ++y) {
            for(var x=0; x<lines[y].length; ++x) {
                arena.set(x, y-1, lines[y][x]);
            }
        }

        var stats = lines[dimensions[1]+2].split(" ");
        var cell = { x: stats[0], y: stats[1], hp: stats[2], energy: stats[3], point: arena.get(stats[0], stats[1]) };

        // decide on an action and write the action to stdout
        decide(arena, cell, function(output) { process.stdout.write(output); })
    }
});

var Arena = function() {
    this.dict = {};
};
Arena.prototype = {
    // get Point object
    get: function(x,y) {
        return this.dict[x+","+y];
    },

    // store Point object
    set: function(x,y,d) {
        this.dict[x+","+y] = new Point(x,y,d);
    },

    // get an array of all Points adjacent to this one whose symbol is contained in matchList
    // if matchList is omitted, return all Points
    getAdjacentMatches: function(point, matchList) {
        var result = [];
        for(var i=-1; i<=1; ++i) {
            for(var j=-1; j<=1; ++j) {
                var inspectedPoint = this.get(point.x+i, point.y+j);
                if(inspectedPoint && 
                   (i!=0 || j!=0) &&
                   (!matchList || matchList.indexOf(inspectedPoint.symbol) != -1)) {
                    result.push(inspectedPoint);
                }
            }
        }
        return result;
    },

    // return the direction from point1 to point2
    getDirection: function(point1, point2) {
        var dx = point2.x - point1.x;
        var dy = point2.y - point1.y;
        dx = Math.abs(dx) / (dx || 1);
        dy = Math.abs(dy) / (dy || 1);

        c2d = { "0,0":"-",
                "0,-1":"N", "0,1":"S", "1,0":"E", "-1,0":"W",
                "-1,-1":"NW", "1,-1":"NE", "1,1":"SE", "-1,1":"SW" };

        return c2d[dx + "," + dy];
    }
}

var Point = function(x,y,d) {
    this.x = x;
    this.y = y;
    this.symbol = d;
}
Point.prototype.toString = function() {
    return "(" + this.x + ", " + this.y + ")";
}

ฉันพยายามทดสอบ แต่ฉันไม่ให้มันวิ่ง ผมติดตั้งพยายาม Node.js node c:/cells/petri.js 'node c:/cells/bomber.js' 'node c:/cells/sample.jsบรรทัดคำสั่ง เมื่อฉันพิมพ์สิ่งนี้ในคอนโซลแอ็พพลิเคชันโหนดฉันเพิ่งได้รับสามจุดเมื่อฉันลองและรันใน windows cmd ฉันจะได้รับ: 'node' ไม่ได้รับการยอมรับว่าเป็นคำสั่งภายในหรือภายนอกโปรแกรมปฏิบัติการหรือแบตช์ไฟล์ ฉันบันทึกไฟล์ทั้งหมดเป็นไฟล์. js ในโฟลเดอร์ที่ถูกต้อง ช่วย noob ได้ไหม? ฉันจะไปที่การแชทหรือแสดงความคิดเห็นที่อื่น แต่ตัวแทนของฉันต่ำเกินไป
overactor

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

ดูเหมือนว่าคุณจะมีประเภทในบรรทัดif((nearbyEnemies.length - nearbyFriendlies.length > 1 ¦¦ - ซึ่ง¦¦ดูเหมือนจะไม่เป็นผู้ดำเนินการที่ถูกต้องและคุณมีวงเล็บที่ไม่ตรงกัน ฉันคิดว่าการจัดรูปแบบรหัสอาจเกิดความสับสนเมื่อคุณโพสต์ข้อความ
apsillers

สิ่งนี้ทำงานได้ค่อนข้างแย่ตามการทดสอบของฉัน คุณมีการมอบหมายจำนวนมาก ( =) เมื่อสิ่งที่คุณต้องการคือการเปรียบเทียบความเท่าเทียมกัน ( ==)
justhalf

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