• # Côté code

    Posté par (Mastodon) . En réponse au message Avent du Code, jour 17. Évalué à 2.

    Ici j'ai pas mal de modélisation de mon Block Tétris.
    Vu l'ampleur de la tâche avant grosse réduction, cf messages précédents, je prévoyais de gagner le moindre micro-cycle d'horloge.
    Donc quand on déplace à gauche, on teste les blocs de gauche, à droite ceux de droite, en bas ceux du bas.

    La Simulation par contre devient complexe entre ma première version et la seconde...

    Le code en brut. C'est bourré d'itérateurs de partout

    import sys
    from collections import deque
    def left(block, board=None):
     block.left(board)
     return True
    def right(block, board=None):
     block.right(board)
     return True
    def down(block, board=None):
     return block.down(board)
    class Block:
     x, y, w, h, X = 2, 0, 0, 0, 6
     color = 1
     def __init__(self, *tiles, color=None):
     self.color = color or self.color
     self.all = tiles
     self.w = max(x for x, y in tiles) + 1
     self.h = max(y for x, y in tiles) + 1
     self.X = 7 - self.w
     self._left = set()
     self._right = set()
     self._down = set()
     for x in range(self.w):
     down = None
     for y in range(self.h):
     if down is None and (x, y) in tiles:
     down = (x, y)
     self._down.add(down)
     for y in range(self.h):
     left = None
     right = None
     for x in range(self.w):
     if left is None and (x, y) in tiles:
     left = (x, y)
     self._left.add(left)
     for x in reversed(range(self.w)):
     if right is None and (x, y) in tiles:
     right = (x, y)
     self._right.add(right)
     def __call__(self, y):
     """Reinit Block position at specific height, x always starts at 2"""
     self.y = y
     self.x = 2
     return self
     def __iter__(self):
     for x, y in self.all:
     yield x + self.x, y + self.y
     @property
     def iright(self):
     for x, y in self._right:
     yield x + self.x, y + self.y
     @property
     def ileft(self):
     for x, y in self._left:
     yield x + self.x, y + self.y
     @property
     def idown(self):
     for x, y in self._down:
     yield x + self.x, y + self.y
     def right(self, board): # move right
     if self.x == self.X:
     return False
     self.x += 1
     if board and self.iright in board:
     self.x -= 1
     return False
     return True
     def left(self, board): # move left
     if self.x == 0:
     return False
     self.x -= 1
     if board and self.ileft in board:
     self.x += 1
     return False
     return True
     def down(self, board): # move down
     if self.y == 0:
     return False
     self.y -= 1
     if board and self.idown in board:
     self.y += 1
     return False
     return True
    class Board:
     def __init__(self, w=7, h=0):
     self.w = w
     self.h = h
     self.dy = 0
     self.map = []
     self.resize()
     def __call__(self, x, y):
     return self.map[y][x]
     def resize(self):
     for _ in range(len(self.map) + self.dy, self.h + 4): # buffer of 4 blank lines
     self.map.append([0 for x in range(self.w)])
     if len(self.map) > 1000:
     self.dy += len(self.map) - 1000
     self.map = self.map[-1000:]
     def insert(self, block):
     for x, y in block:
     self.map[y - self.dy][x] = block.color
     self.h = max(self.h, block.y + block.h)
     self.resize()
     def __contains__(self, block):
     for x, y in block:
     if self.map[y - self.dy][x]:
     return True
     return False
    class Simulation:
     def __init__(self, data, minblocks=2022, maxblocks=1000000000000):
     # Initialize things
     self.maxblocks = maxblocks
     self.minblocks = minblocks
     self.imoves = self.itermoves(data)
     self.iblocks = self.iterblocks()
     self.board = Board()
     self.heights = deque([0])
     self.history = dict()
     self.boardhash = dict()
     self.boardsnapshot = dict()
     self.count = 0
     self.start()
     def start(self):
     # Search for the first repeated full cycle
     self.searchfirstcycle()
     self.startlen = self.cycleend - 2 * self.cyclelen
     self.nbcycle, self.endlen = divmod(
     self.maxblocks - self.cycleend,
     self.cyclelen
     )
     self.extremities = self.cycleend + self.endlen
     while self.count < max(self.extremities, self.minblocks):
     self.iteration()
     self.startheight = self.heights[self.startlen]
     self.cycleheight = self.heights[self.cycleend] - self.heights[self.cyclestart]
     def searchfirstcycle(self):
     while True:
     status = self.iteration() # , str(self.board.map[-999:-5])
     if status in self.history:
     # We may have a cycle, take a full snapshot !
     heightstart = self.heights[self.history[status]]
     heightend = self.heights[self.count]
     cycleboard = str(self.board.map[heightstart - heightend - 5:-5])
     cyclehash = hash(cycleboard)
     if(self.boardhash.get(status, False) == cyclehash
     and self.boardsnapshot.get(status, False) == cycleboard):
     self.cyclestart = self.history[status] - 1
     self.cycleend = self.count - 1
     self.cyclelen = self.cycleend - self.cyclestart
     return
     self.boardhash[status] = cyclehash
     self.boardsnapshot[status] = cycleboard
     self.history[status] = self.count
     def iteration(self):
     block = self.nextblock(self.board.h + 3)
     for _ in range(7): # 7 automatic moves, no board implied
     self.nextmove(block)
     while self.nextmove(block, self.board):
     pass
     self.board.insert(block)
     self.count += 1
     self.heights.append(self.board.h)
     return self.idblock, self.idmove
     def itermoves(self, input):
     _moves = [left if c == '<' else right for c in input]
     while True:
     for id, move in enumerate(_moves):
     self.idmove = id
     yield move
     yield down
     @property
     def nextmove(self):
     return next(self.imoves)
     def iterblocks(self):
     b1 = Block((0, 0), (1, 0), (2, 0), (3, 0), color=1)
     b2 = Block((0, 1), (1, 0), (1, 1), (1, 2), (2, 1), color=2)
     b3 = Block((0, 0), (1, 0), (2, 0), (2, 1), (2, 2), color=3)
     b4 = Block((0, 0), (0, 1), (0, 2), (0, 3), color=4)
     b5 = Block((0, 0), (0, 1), (1, 0), (1, 1), color=5)
     while True:
     self.idblock = 0
     yield b1
     self.idblock = 1
     yield b2
     self.idblock = 2
     yield b3
     self.idblock = 3
     yield b4
     self.idblock = 4
     yield b5
     @property
     def nextblock(self):
     return next(self.iblocks)
    s = Simulation(sys.stdin.read().strip())
    print("""
    [demo] Height of 2022 blocks: 3068
    [demo] Cycle of 35 blocks, 1 Cycle 60, 2 Cycles 113, Cycle height 53, Extremities height 131
    [demo] Total Height = 1514285714288
    [real] Height of 2022 blocks: 3153
    [real] Cycle of 1705 blocks, 1 Cycle 2648, 2 Cycles 5297, Cycle height 2649, Extremities height 7766
    [real] Total Height = 1553665689155
    """)
    print(f"2022 Blocks : {s.heights[2022]}")
    print(f"First part : {s.startlen}[{s.heights[s.startlen]}]")
    print(f"One Cycle : {s.cyclelen}[{s.cycleheight}]")
    print(f"Extremities : {s.extremities}[{s.heights[s.extremities]}]")
    s.totalheight = s.heights[s.extremities] + s.nbcycle * s.cycleheight
    print(f"Total Height : {s.totalheight}")
    • Yth.