เล่นโอเอกซ์และไม่แพ้


14

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

ท้าทาย

เขียนโปรแกรมที่เล่นเกมโอเอกซ์ ต้องไม่แพ้ (ดังนั้นควรจบเกมด้วยการเสมอหรือโดยการชนะ)

อนุญาตให้ใช้วิธี I / O

  1. อินพุตอาจเป็นบอร์ดปัจจุบัน คุณสามารถสันนิษฐานได้ว่าการเคลื่อนไหวก่อนหน้าของผู้เล่นคนที่ 2 ทั้งหมดถูกเล่นโดยเครื่องยนต์ของคุณ
  2. อินพุตอาจเป็นการเคลื่อนไหวของผู้เล่นคนแรกและฟังก์ชั่นของคุณจะเก็บการเคลื่อนไหวที่เกิดขึ้นในอดีต ในกรณีนี้ฟังก์ชั่นนี้เรียกว่าหลายครั้งหนึ่งครั้งต่อการเคลื่อนไหวแต่ละครั้ง หรืออินพุต / พรอมต์ของฟังก์ชัน / โปรแกรมหลายครั้ง
  3. คุณได้รับอนุญาตให้ป้อนข้อมูลเพิ่มเติมโดยบอกว่าคุณเป็นผู้เล่นคนแรกหรือเขียนสองฟังก์ชั่น (อาจเกี่ยวข้องกัน) เพื่อแก้ปัญหาผู้เล่นคนแรกและผู้เล่นคนที่สอง หากโปรแกรมของคุณต้องการใช้วิธีการป้อนข้อมูล 2 (การโทรหลายสาย) คุณสามารถตัดสินใจได้ว่าจะส่งสิ่งใดในการโทรครั้งแรก
  4. เอาท์พุทอาจเป็นคณะกรรมการหลังจากที่คุณเปิด
  5. เอาท์พุทอาจจะย้ายของคุณ
  6. การย้ายอาจแสดงเป็นคู่ของตัวเลข (สามารถเป็นดัชนี 0 หรือดัชนี 1 ดัชนี) ตัวเลขในช่วง 0 ~ 8 หรือตัวเลขในช่วง 1 ~ 9
  7. บอร์ดอาจแสดงเป็นอาร์เรย์ 3 × 3 หรืออาร์เรย์ที่มีความยาว 9 แม้ว่าภาษานั้นจะมีอาร์เรย์ 0 การจัดทำดัชนีคุณสามารถใช้การจัดทำดัชนี 1 รายการได้
  8. เซลล์ในตารางอาจจะใช้ค่าที่แตกต่างใด ๆ 3 เพื่อบ่งชี้X, Oและว่างเปล่า

เกณฑ์การชนะ

รหัสสั้นที่สุดในแต่ละภาษาชนะ


หากมีการสูญเสียให้กับคุณแสดงว่าโซลูชันของคุณไม่ถูกต้อง คุณกำลังเล่นกับคนอื่นดังนั้นกระดานหมากรุกจะไม่เปลี่ยนแปลงทันทีดังนั้นwe can assume that all previous moves of the 2nd player were also played by our engine
l4m2


1
@ l4m2 เพิ่งรีสตาร์ทล่าม เสร็จสิ้น ทำไมต้องกังวลกับมัน? มันเพิ่มจำนวนไบต์ไปโดยไม่จำเป็น
user202729

1
อย่าลืมว่าคุณไม่ควรเปลี่ยนความท้าทายในความคิดเห็น
user202729

4
อย่าทำโบนัส อาจจำเป็นต้องใช้หรือลบออกอย่าทำให้เป็นทางเลือก โบนัสซากปรักหักพังท้าทาย ..
Rɪᴋᴇʀ

คำตอบ:


4

Befunge, 181 168 ไบต์

>>4&5pp20555>>03>16p\::5g8%6p5v
 ^p5.:g605$_ #!<^_|#:-1g61+%8g<
543217539511|:_^#->#g0<>8+00p3+5%09638527419876
v<304p$_v#:->#$$:2`#3_:^
>#\3#13#<111v124236478689189378

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

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

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

ผู้ใช้เล่นก่อน: ลองออนไลน์!
คอมพิวเตอร์เล่นก่อน: ลองออนไลน์!

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

ผู้ใช้เล่นก่อน: ลองออนไลน์!
คอมพิวเตอร์เล่นก่อน: ลองออนไลน์!

โปรดทราบว่าคุณจะต้องรอให้ TIO หมดเวลาก่อนจึงจะเห็นผลลัพธ์

คำอธิบาย

บอร์ดจะถูกเก็บไว้ในพื้นที่หน่วยความจำ Befunge เป็นค่าคงที่จำนวน 9 ค่าซึ่งจัดทำดัชนีตั้งแต่ 1 ถึง 9 ซึ่งช่วยให้เราใช้ออฟเซ็ตศูนย์เป็นกรณีพิเศษ "ไม่มีการเคลื่อนที่" เมื่อเราต้องการให้คอมพิวเตอร์เล่นก่อน การเคลื่อนไหวของผู้เล่นถูกเก็บไว้เป็น 4 และการเคลื่อนไหวของคอมพิวเตอร์เป็น 5 ในการเริ่มต้นด้วยตำแหน่งทั้งหมดจะถูกกำหนดเป็น 32 (ค่าเริ่มต้นของหน่วยความจำ Befunge) ดังนั้นเมื่อใดก็ตามที่เราเข้าถึงบอร์ดเราแก้ไขด้วย 8 ดังนั้นเราจะกลับมาเป็น 0, 4 หรือ 5

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

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

รายการหลักของอเนกประสงค์ที่เราทดสอบคือชุดค่าผสมที่ชนะ (1/2/3, 1/5/9, 1/4/7 ฯลฯ ) อันดับแรกเรามองหาทั้งหมด 10 (คอมพิวเตอร์กำลังจะชนะ) และจากนั้นทั้งหมด 8 (ผู้เล่นกำลังจะชนะและเราต้องป้องกันการย้ายนั้น) เห็นได้ชัดน้อยกว่าเราตรวจสอบทั้งหมด 9 (ถ้าผู้เล่นและคอมพิวเตอร์แต่ละคนมีหนึ่งในตำแหน่งมันเป็นกลยุทธ์ที่ดีสำหรับคอมพิวเตอร์ที่จะใช้ที่สาม)

ก่อนที่จะถึงสถานการณ์ที่ผ่านมาการเคลื่อนไหวเชิงกลยุทธ์อื่น ๆ ที่เราทำคือการตรวจสอบชุดมุมทั้งหมด (1/2/4, 2/3/6 ฯลฯ ) รวมถึงชุดค่าผสมมุมสองชุดที่ตรงข้ามกัน (1/8/9 และ 3 / 7/8) หากชุดค่าผสมเหล่านี้รวมถึง 8 นั่นคือผู้เล่นได้รับตำแหน่งสองตำแหน่งมันเป็นกลยุทธ์ที่ดีสำหรับคอมพิวเตอร์ที่จะรับตำแหน่งฟรีที่เหลืออยู่

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

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


ฉันไม่ค่อยรู้จัก Befunge แต่บางทีคุณสามารถทดสอบอินพุตที่เป็นไปได้ทั้งหมด ( ตัวอย่าง )
l4m2

@ l4m2 FYI ตอนนี้ฉันได้เรียกใช้สคริปต์ทดสอบที่พยายามทุกวิถีทางที่เป็นไปได้กับคอมพิวเตอร์และสามารถยืนยันได้ว่ามันจะไม่มีวันหมด
James Holderness

2

Python 2: 399 401 349 333 317 370 ไบต์

2x แก้ไขข้อผิดพลาด: เครดิตถึง l4m2

-52 ตัวอักษร: มอบเครดิตให้กับ undergroundmonorail

-16 ตัวอักษร: มอบเครดิตให้แก่ Jonathan Frech

-26 ตัวอักษร: เครดิตสำหรับผู้ใช้ 2,02729

def f(b):
 t=4,9,2,3,5,7,8,1,6;n=lambda k:[t[i]for i,j in enumerate(b)if j==k];p,o,a,I=n(2),n(1),n(0),t.index
 for i in p:
    for j in p:
     for k in a:
        if i+j+k==15and-j+i:return I(k)
 for i in o:
    for j in o:
     for k in a:
        if i+j+k==15and-j+i:return I(k)
 for i in 9,3,7,1:
    if i in a and 5 in p:return I(i)
 for i in 5,4,2,8,6:
    if i in a:return I(i)
 return I(a[0])

ลองออนไลน์!

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

4 | 9 | 2
--+---+--
3 | 5 | 7
--+---+--
8 | 1 | 6

จากนั้นการรับสามในแถวจะเท่ากับการเลือกตัวเลขสามตัวในช่วง [1,9] ที่รวมกันเป็น 15 คำตอบนี้ใช้ประโยชน์จากความคิดนี้ ฟังก์ชั่นใช้รายการที่มีตัวเลขเก้าตัวแทนบอร์ด 0 หมายถึงพื้นที่ว่าง 1 ถูกครอบครองโดยฝ่ายตรงข้ามและ 2 หมายถึงการเล่นครั้งก่อนโดยโปรแกรม 3 บรรทัดแรกแสดงจำนวนโปรแกรมที่เลือก (p) ฝ่ายค้านเลือก (o) และยังคงมีอยู่ (a) จากนั้นจะตรวจสอบหมายเลขที่มีอยู่และดูว่ามีหมายเลขใดบ้างรวมกับตัวเลขสองหมายเลขที่ได้เลือกเพิ่มไปสิบห้า ถ้าเป็นเช่นนั้นมันจะเลือกสี่เหลี่ยมนั้นและชนะ หากไม่มีการย้ายที่ชนะทันทีมันจะตรวจสอบเพื่อดูว่าฝ่ายตรงข้ามสามารถชนะโดยใช้วิธีการเดียวกัน หากพวกเขาทำได้มันจะต้องใช้เวลาในการชนะของพวกเขา หากไม่มีการชนะหรือการปิดกั้นการเคลื่อนไหว มันจะย้ายในมุม วิธีนี้ช่วยป้องกันคนโง่:

- - - 
- X -
- - -

- O -             # Bad Move
- X -
- - -

- O X
- X -
- - -

- O X
- X -
O - -

- O X
- X -
O - X

หากไม่มีสถานการณ์เหล่านี้เกิดขึ้นก็จะเลือกสแควร์โดยพลการ ฟังก์ชั่นส่งออกตัวเลข [0,8] ซึ่งเป็นตัวแทนของสี่เหลี่ยมจัตุรัสดัชนี 0 ที่เลือกโดยอัลกอริทึม

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

แก้ไข: เพื่อชี้แจงฟังก์ชั่นที่ใช้ในคณะกรรมการในรูปแบบของอาร์เรย์และส่งออกย้ายเป็นจำนวนเต็มใน [0,8] เนื่องจากกลยุทธ์ I / O นี้เป็น clunky ดังนั้นนี่คือสคริปต์ตัวห่อหุ้มที่ทำให้โต้ตอบได้มากขึ้น ใช้อาร์กิวเมนต์บรรทัดคำสั่งเดียวซึ่งควรเป็น 1 หากผู้เล่นไปก่อนและ 0 ถ้าโปรแกรมไปก่อน

import sys

def f(b):
 t=4,9,2,3,5,7,8,1,6;n=lambda k:[t[i]for i,j in enumerate(b)if j==k];p,o,a,I=n(2),n(1),n(0),t.index
 for i in p:
    for j in p:
     for k in a:
        if i+j+k==15and-j+i:return I(k)
 for i in o:
    for j in o:
     for k in a:
        if i+j+k==15and-j+i:return I(k)
 for i in 9,3,7,1:
    if i in a and 5 in p:return I(i)
     for i in 5,4,2,8,6:
        if i in a:return I(i)
 return I(a[0])

board = [0,0,0,0,0,0,0,0,0]
rep = {0:"-",1:"X",2:"O"}

turn = int(sys.argv[1])
while True:
    for i in range(3):
        print rep[board[i*3]]+" "+rep[board[i*3+1]]+" "+rep[board[i*3+2]]
        print
    if turn:
        move = int(raw_input("Enter Move [0-8]: "))
    else:
        move = f(board)
    board[move] = turn+1
    turn = (turn+1)%2 


1
ทุกreturnบรรทัดของคุณยกเว้นบรรทัดสุดท้ายที่สามารถใส่ในบรรทัดก่อนที่พวกเขาบันทึกช่องว่าง
undergroundmonorail

1
นอกจากนี้ผมไม่สามารถช่วย แต่สงสัยว่ามันจะประหยัดไบต์แทนการทำe=enumerateทำf=lambda n:[t[i]for i,j in enumerate(b)if j==n]และกำหนดp, oและaการใช้ฟังก์ชั่น ยังไม่ได้นับแม้ว่า
undergroundmonorail

3
ยังคงถูกแฮ็ก xkcd.com/832ช่วยได้จริงๆ
l4m2

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