ค้นหาการเริ่มต้นที่ดีที่สุดของ Chomp


14

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

  1. อินพุตคือขนาดของสี่เหลี่ยมผืนผ้า (ตัวเลขสองตัว)
  2. เอาท์พุทเป็นที่ตั้งของการย้ายที่ชนะ (ตัวเลขสองตัว)
  3. หากมีการชนะมากกว่าหนึ่งครั้งคุณสามารถส่งออกได้เลย

นี่คือรหัสกอล์ฟ รหัสที่สั้นที่สุด (ทุกภาษา) ชนะ

ตัวอย่าง

หมายเหตุ: ผลลัพธ์เป็นเพียงตัวเลขสองตัว ASCII art ด้านล่างเป็นเพียงการแสดงให้เห็นถึงความหมายของตัวเลข

อินพุต: 5 3 (ดัชนีเริ่มต้นที่ 1 จากมุมซ้ายล่าง)

ผลลัพธ์: 4 3

XXX--
XXXXX
XXXXX

อินพุต: 4 4

ผลลัพธ์: 2 2

X---
X---
X---
XXXX

โบนัส

ลบ 15 ตัวอักษรจากคะแนนของคุณหากคุณแสดงท่าที่ชนะทั้งหมด ตัวเลขแต่ละคู่จะต้องคั่นด้วยการขึ้นบรรทัดใหม่


ในตัวอย่างแรกของคุณฉันคิดว่าคุณมีขีดกลางมากเกินไปหนึ่งชุด
kitcar2000

@Kitcar คุณพูดถูก แก้ไขแล้ว.
Ypnypn

ฉันไม่เข้าใจรูปแบบผลลัพธ์ ตัวเลขเหล่านั้นสอดคล้องกับตำแหน่งเหล่านั้นอย่างไร
undergroundmonorail

@undergroundmonorail ดัชนีที่ใช้ 1 จากด้านล่างซ้าย ดัชนีแรกคือแกนนอนและดัชนีที่สองคือดัชนีแนวตั้ง
Martin Ender

2
ในการตอบสนองต่อเงินรางวัลของคุณ: ในหมากรุกคุณมีการเคลื่อนไหวน้อยกว่า 119 ครั้งในเวลาใดก็ตาม (โดยปกติจะน้อยกว่า) และไม่มีซุปเปอร์คอมพิวเตอร์มาจนถึงทุกวันนี้ในการแก้ปัญหาหมากรุกโดยใช้อัลกอริทึมที่รู้จักกันดีที่สุด ในกริด Chomp 10 ถึง 10 มีการเคลื่อนไหวครั้งแรกที่เป็นไปได้ 100 ครั้งและแต่ละอันมีการเคลื่อนไหวที่สอง 1-99 ครั้ง อะไรที่ทำให้คุณคิดว่ามันง่ายที่จะดุร้าย ฉันขอแนะนำให้ จำกัด ขนาดกริดของคุณหากคุณต้องการคำตอบที่ดุร้าย แก้ไข: แต่ไม่ทำอย่างนั้น การเปลี่ยนแปลงข้อกำหนดกลางคันการประกวดไม่ดี
ฝนตก

คำตอบ:


7

GolfScript, 82 ( 108 97 ตัวอักษร - 15 โบนัส)

~),1/{{:F$0=),{F\+}/}%}@(*(0*\{1${1$\{\(@<},=},{1$\{\(@>},+(-!},:Y!{.,/+0}*;}/;Y{.-1=.@?)' '@)n}/

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

ตัวอย่าง:

> 5 3
4 3

> 5 4
3 3

> 6 6
2 2

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

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

~                   # Evaluate the input (stack is now w h)

# BUILDING THE COMPLETE STATE SPACE
# Iteratively builds the states starting with 1xh board, then 2xh board, ...

),1/                # Generate the array [[0] [1] ... [h]] which is the space for 1xh
{                   # This loop is now ran w-1 times and each run adds all states for the 
                    # board with one additional column
  {                 # The {}/] block simply runs for each of the existing states
    :F$0=           #   Take the smallest entry (which has to be the last one)
    ),              #   For the last column all values 0..x are possible
    {F\+}/          #     Append each of these values to the smaller state
  }%
}@(*

# The order ensures that the less occupied boards are first in the list.
# Thus each game runs from the end of the list (where [h h ... h] is) to 
# the start (where [0 0 ... 0] is located).

# RUN THROUGH THE SEARCH SPACE
# The search algorithm therefore starts with the empty board and works through all
# possible states by simply looping over this list. It builds a list of those states
# which are known as non-winning states, i.e. those states where a player should 
# aim to end after the move

(                   # Skips the empty board (which is a winning configuration)
0*\                 # and makes an empty list out of it (which will be the list of
                    # known non-winning states (initially empty))
{                   # Loop over all possible states
  1$                #   Copy of the list of non-winning states
  {                 #   Filter those which are not reachable from the current state,
                    #   because at least one column has more pieces that the current
                    #   board has
    1$\{\(@<},=
  },
  {                 #   Filter those which are not reachable from the current state,
                    #   because no valid move exists
    1$\{\(@>},+     #     Filter those columns which are different between start and
                    #     end state
    (-!             #     If those columns are all of same height it is possible to move
  },
  :Y                #   Assign the result (list of all non-winning states which are
                    #   reachable from the current configuration within one move)
                    #   to variable Y

  !{                #   If Y is non-empty this one is a winning move, otherwise 
                    #   add it to the list
    .,/+
    0               #     Push dummy value
  }*;
}/
;                   # Discard the list (interesting data was saved to variable Y)

# OUTPUT LOOP
# Since the states were ordered the last one was the starting state. The list of 
# non-winning states were saved to variable Y each time, thus the winning moves 
# from the initial configuration is contained in this variable.

Y{                  # For each item in Y
  .-1=.@?)          #   Get the index (1-based) of the first non-h value
  ' '               #   Append a space
  @)                #   Get the non-h value itself (plus one)
  n                 #   Append a newline
}/

+1 สำหรับการแก้ปัญหาด้วยตัวเองและสำหรับรหัสที่มีความคิดเห็นดีมาก
Xuntar

การเขียนโปรแกรมแบบไดนามิกจากล่างขึ้นบนมากกว่าจากบนลงล่าง ดี ฉันถือว่าการทำเช่นนั้น แต่การสร้างสถานะในลำดับที่ถูกต้องสำหรับการสำรวจเส้นทางจากล่างขึ้นบนนั้นทำงานได้มากกว่าและสับสนมากกว่าการค้นหาแบบเรียกซ้ำ ฉันแปลกใจที่รหัสจบลงนานมาก ฉันคาดว่าภาษาสั้น ๆ เช่น Golfscript น่าจะเป็นทางออกที่สั้นกว่ามาก
user2357112 รองรับ Monica

ดีมากและคิดออกดี
Mouq

8

Python 2 3, 141-15 = 126

def win(x,y):w([y]*x)
w=lambda b,f=print:not[f(r+1,c+1)for r,p in enumerate(b)for c in range(p)if(r+c)*w(b[:r]+[min(i,c)for i in b[r:]],max)]

การค้นหาขั้นต่ำแบบ Brute-force สำหรับการเคลื่อนไหวที่เป็นไปได้ทุกครั้งเราจะตรวจสอบซ้ำว่าฝ่ายตรงข้ามสามารถชนะได้หลังจากที่เราทำการเคลื่อนที่นั้นหรือไม่ ค่อนข้างอ่อนกอล์ฟ คนอื่นน่าจะทำได้ดีกว่านี้มาก รู้สึกเหมือนเป็นงานของ APL

  • winเป็นส่วนต่อประสานสาธารณะ wมันต้องใช้เวลาขนาดของบอร์ดแปลงให้เป็นตัวแทนคณะกรรมการและผ่านที่
  • wเป็นอัลกอริทึม minimax มันต้องใช้สถานะของบอร์ดพยายามย้ายทั้งหมดสร้างรายการที่มีองค์ประกอบที่สอดคล้องกับการเคลื่อนไหวที่ชนะและส่งกลับค่าจริงถ้ารายการว่างเปล่า ด้วยค่าเริ่มต้นการf=printสร้างรายการมีผลข้างเคียงของการพิมพ์การย้ายที่ชนะ ชื่อฟังก์ชั่นที่ใช้ในการทำให้รู้สึกมากขึ้นเมื่อมันกลับรายการของการเคลื่อนไหวที่ชนะ แต่แล้วฉันย้ายไปnotข้างหน้าของรายการเพื่อประหยัดพื้นที่
  • for r,p in enumerate(b)for c in xrange(p) if(r+c): ซ้ำทุกการเคลื่อนไหวที่เป็นไปได้ 1 1จะถือว่าเป็นการย้ายที่ไม่ถูกกฎหมายทำให้กรณีฐานง่ายขึ้นเล็กน้อย
  • b[:r]+[min(i,c)for i in b[r:]]: สร้างรัฐของคณะกรรมการหลังจากที่ย้ายที่แสดงโดยพิกัดและrc
  • w(b[:r]+[min(i,c)for i in b[r:]],max): เรียกคืนเพื่อดูว่าสถานะใหม่เป็นสถานะที่สูญหายหรือไม่ maxเป็นฟังก์ชั่นที่สั้นที่สุดที่ฉันสามารถหาได้ว่าจะรับอาร์กิวเมนต์จำนวนเต็มสองข้อและไม่บ่น
  • f(r+1,c+1): หากfมีการพิมพ์ให้พิมพ์การย้าย ไม่ว่าfจะเป็นอะไรก็ตามมันจะสร้างมูลค่าเพื่อลดความยาวของรายการ
  • not [...]: notส่งคืนTrueรายการว่างเปล่าและFalseไม่ว่างเปล่า

รหัส Python 2 ดั้งเดิมที่ไม่สมบูรณ์รวมถึงการบันทึกเพื่อจัดการอินพุตที่ใหญ่กว่ามาก:

def win(x, y):
    for row, column in _win(Board([y]*x)):
        print row+1, column+1

class MemoDict(dict):
    def __init__(self, func):
        self.memofunc = func
    def __missing__(self, key):
        self[key] = retval = self.memofunc(key)
        return retval

def memoize(func):
    return MemoDict(func).__getitem__

def _normalize(state):
    state = tuple(state)
    if 0 in state:
        state = state[:state.index(0)]
    return state

class Board(object):
    def __init__(self, state):
        self.state = _normalize(state)
    def __eq__(self, other):
        if not isinstance(other, Board):
            return NotImplemented
        return self.state == other.state
    def __hash__(self):
        return hash(self.state)
    def after(self, move):
        row, column = move
        newstate = list(self.state)
        for i in xrange(row, len(newstate)):
            newstate[i] = min(newstate[i], column)
        return Board(newstate)
    def moves(self):
        for row, pieces in enumerate(self.state):
            for column in xrange(pieces):
                if (row, column) != (0, 0):
                    yield row, column
    def lost(self):
        return self.state == (1,)

@memoize
def _win(board):
    return [move for move in board.moves() if not _win(board.after(move))]

การสาธิต:

>>> for i in xrange(7, 11):
...     for j in xrange(7, 11):
...         print 'Dimensions: {} by {}'.format(i, j)
...         win(i, j)
...
Dimensions: 7 by 7
2 2
Dimensions: 7 by 8
3 3
Dimensions: 7 by 9
3 4
Dimensions: 7 by 10
2 3
Dimensions: 8 by 7
3 3
Dimensions: 8 by 8
2 2
Dimensions: 8 by 9
6 7
Dimensions: 8 by 10
4 9
5 6
Dimensions: 9 by 7
4 3
Dimensions: 9 by 8
7 6
Dimensions: 9 by 9
2 2
Dimensions: 9 by 10
7 8
9 5
Dimensions: 10 by 7
3 2
Dimensions: 10 by 8
6 5
9 4
Dimensions: 10 by 9
5 9
8 7
Dimensions: 10 by 10
2 2

สำหรับ13x13การใช้เวลา2x2และคุณต้องการชนะ
davidsbro

@davidsbro: ใช่นั่นเป็นท่าที่ชนะสำหรับกระดานสี่เหลี่ยมใด ๆ ที่ใหญ่กว่า 1x1 แต่รหัสของฉันยังไม่ได้คำนวณ
user2357112 รองรับ Monica

2

Perl 6: 113 108 ตัวอักษร - 15 = 93 คะแนน

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

sub win(*@b){map ->\i,\j{$(i+1,j+1) if @b[i][j]&&!win @b[^i],@b[i..*].map({[.[^j]]})},(^@b X ^@b[0])[1..*]}

มันใช้งานได้เหมือนกับการใช้งาน Python ของ @ user2357112 (ขึ้นอยู่กับเขา / เธอฉันไม่สามารถคิดได้ถ้าไม่มีงานของเขา / เธอ!) ยกเว้น win () ใช้ Chomp board (array) แทนความกว้างและความยาว มันสามารถใช้เช่น:

loop {
    my ($y, $x) = get.words;
    .say for @(win [1 xx $x] xx $y)
}

เวอร์ชันที่มีการบันทึกความจำซึ่งสามารถจัดการอินพุตที่เหมาะสม (ไม่ได้รับการปรับให้เหมาะสมสำหรับการอ่าน):

my %cache;
sub win (*@b) {
    %cache{
        join 2, map {($^e[$_]??1!!0 for ^@b[0]).join}, @b
    } //= map ->\i,\j{
        $(i+1,j+1) if @b[i][j] and not win
            @b[^i], @b[i..*].map({[.[^(* min j)]]}).grep: +*;
    },(^@b X ^@b[0])[1..*]
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.