在一系列字段范围内进行检查

时间:2017-11-14 18:28:09

标签: python python-3.x list

我不太确定如何标题,对不起,如果它没有意义/有误导性。

注意 - "船"在数组中彼此相邻3个,所以

_|O|_  _|_|_
_|O|_  O|O|O
 |O|    | |

是船。

所以我有一个列表(n x n)(使用列表)列表,其中我在随机空间生成n个船。我不希望船只彼此相邻,靠近拐角,或者彼此叠加。

试图检查一艘船是否最终会停在另一艘船的顶部:

if board[y - 2][x] == 'O' or board[y + 2][x] == 'O' ...

等等,结果不足为奇。

我还有索引超出范围错误,因为我有时会检查不在该字段中的坐标。

那么,有没有一种方法可以在不超出指数范围的情况下检查各个方向的船只?

更好的是,关于如何使船只彼此相邻的任何想法都没有?

船只生成代码在这里:

from random import *

side = int(input())
game_state = []

def generate_initial_state():

    for i in range(side):
        game_state.append([])
        for j in range(side):
            game_state[i].append('.')

    for i in range(side):
        # Generate boat origin on random coordinates within the game board,
        # if there's a boat already, generate new ones
        y_cor = randint(0, side-1)
        x_cor = randint(0, side-1)
        while game_state[y_cor][x_cor] == 'O':
            y_cor = randint(0, side - 1)
            x_cor = randint(0, side - 1)
        # Direct chooses if the boat will be generated up, down, or sideways
        direct = randint(1, 4)
        cycle = 0

        while cycle < 3:
        # Generates a boat going from origin in one direction,
        # if the boat would end outside the board, chooses a different direction
            if direct == 1:
                if y_cor + 2 >= side:
                    direct = randint(1, 4)
                else:
                    game_state[y_cor + cycle][x_cor] = 'O'
                    cycle += 1
            elif direct == 2:
                if x_cor + 2 >= side:
                    direct = randint(1, 4)
                else:
                    game_state[y_cor][x_cor + cycle] = 'O'
                    cycle += 1
            elif direct == 3:
                if y_cor - 2 < 0:
                    direct = randint(1, 4)
                else:
                    game_state[y_cor - cycle][x_cor] = 'O'
                    cycle += 1
            elif direct == 4:
                if x_cor - 2 < 0:
                    direct = randint(1, 4)
                else:
                    game_state[y_cor][x_cor - cycle] = 'O'
                    cycle += 1

for i in range(side):
    print(*game_state[i])

3 个答案:

答案 0 :(得分:2)

首先我只使用两个方向(水平和垂直),这不应该改变概率(使用你的模型,船可以用两种方式生成)。

这允许仅通过超出允许的索引来发生索引溢出,这会引发可被拦截的IndexError(使用否定索引不会导致生成器混乱)。

其次,使用旗帜可以帮助你做到这一点。

我添加了一些其他修改:

编辑:我刚刚意识到我的代码拒绝完全有效的船只,如果他们在边境,所以这是一个没有的版本。

更新:一些解释

我们使用布尔标志boat_built来跟踪随机选择的船位置的适用性:一旦完成所有测试,该变量决定选择是否合适(True)或障碍物在进行测试时遇到(False)。

使用

boat_built &= (game_state[x_cor + k*dx][y_cor + k*dy] != "0")

我们更新每个测试的标记:如果boat_built在测试之前为False,则无论测试结果如何(False),它都将保持False & a = False:这是意图,因为这意味着已经遇到阻塞并且船只无效。

另一方面,如果boat_built在测试之前是True,它将包含之后测试的结果(True & a = a):这也是有意的,因为失败了测试意味着我们现在发现了障碍物。

请注意,即使在早期遇到障碍物,也会对每艘船进行所有15次测试。

from random import *

side = int(input())

game_state = [['.' for i in range(side)] for j in range(side)]

l_dir = [(1, 0), (0, 1)]

def generate_initial_state():

    for i in range(side):
        boat_built = False
        while not boat_built:
            boat_built = True

            y_cor = randrange(side)
            x_cor = randrange(side)

            dx, dy = l_dir[randrange(2)]

            try:
                # check that the three required cells are empty
                for k in range(3):
                    boat_built &= (game_state[x_cor + k*dx][y_cor + k*dy] != "0")
            except IndexError:
                # if any is out of range, choice is invalid
                boat_built = False

            for k in range(5):
                for l in [-1, 1]:
                    try:
                        # check if neighbours on the long sides are empty
                        boat_built &= (game_state[x_cor + (k-1)*dx + l*dy][y_cor + l*dx + (k-1)*dy] != "0")
                    except IndexError:
                        # if we're out of range, no obstruction
                        pass

            for k in [-1, 3]:
                try:
                    # check if neighbours on the short sides are empty
                    boat_built &= (game_state[x_cor + k*dx][y_cor + k*dy] != "0")
                except IndexError:
                    # again, if we're out of range, no obstruction
                    pass


        # if we reach this point, a valid position has been found
        for k in range(3):
            game_state[x_cor + k*dx][y_cor + k*dy] = "0"


generate_initial_state()

for i in range(side):
    print(*game_state[i])

答案 1 :(得分:1)

当你抱怨时,你的代码有太多乏味的重复逻辑来检查一个点的邻居。您可以使用以下内容将四个测试转换为一个:

    offset = [
        (0, -1),
        (-1, 0), (1, 0),
        (0, 1),
    ]
    for xoff, yoff in offset:
        if game_state[x + xoff * cycle][y + yoff * cycle] == 'O':
            report_collision(x, y)

此外,您可以使用'O'标记网格&#34; boat&#34;和'o'和#34;接壤船&#34;,以简化检测相邻船只。

答案 2 :(得分:1)

您可以尝试以下课程,看看它是否解决了您的问题:

#! /usr/bin/env python3
import collections
import enum
import random


def main():
    board = Board(10, 10)
    print(board)
    board.place_boats([2, 3, 3, 4, 5])
    print('\n' + '=' * 21 + '\n')
    print(board)


Point = collections.namedtuple('Point', 'x, y')
# noinspection PyArgumentList
Orientation = enum.Enum('Orientation', 'HORIZONTAL, VERTICAL')


class Board:
    def __init__(self, width, height):
        self.__width = width
        self.__height = height
        self.__matrix = [[False] * height for _ in range(width)]
        self.__available = {Point(x, y)
                            for x in range(width)
                            for y in range(height)}

    def __str__(self):
        width = self.__width * 2 + 1
        height = self.__height * 2 + 1
        grid = [[' '] * width for _ in range(height)]
        for yo, xo, character in (0, 1, '|'), (1, 0, '-'), (1, 1, '+'):
            for y in range(yo, height, 2):
                for x in range(xo, width, 2):
                    grid[y][x] = character
        for x, column in enumerate(self.__matrix):
            for y, cell in enumerate(column):
                if cell:
                    grid[y << 1][x << 1] = '#'
        return '\n'.join(''.join(row) for row in grid)

    # noinspection PyAssignmentToLoopOrWithParameter
    def place_boats(self, sizes, patience=10):
        matrix_backup = [column.copy() for column in self.__matrix]
        available_backup = self.__available.copy()
        for _ in range(patience):
            # try to place all the boats
            for size in sizes:
                for _ in range(patience):
                    # try to place boat of current size
                    point = random.choice(tuple(self.__available))
                    method = random.choice(tuple(Orientation))
                    try:
                        # try to place a boat; does not mangle the matrix
                        self.make_boat(point, size, method)
                    except RuntimeError:
                        pass
                    else:
                        # break out of inner patience loop; go to next size
                        break  # on success
                else:
                    # break to outer patience loop; start from beginning
                    self.__matrix = [column.copy() for column in matrix_backup]
                    self.__available = available_backup.copy()
                    break  # on failure
            else:
                # break out of outer patience loop; all sizes were placed
                break  # on success
        else:
            raise RuntimeError('could not place the requested boats')

    def make_boat(self, point, size, method):
        backup = [column.copy() for column in self.__matrix]
        unusable = set()
        for offset in range(size):
            if method is Orientation.HORIZONTAL:
                block = self.mark_cell(point, x_offset=offset)
            elif method is Orientation.VERTICAL:
                block = self.mark_cell(point, y_offset=offset)
            else:
                raise ValueError('method was not understood')
            if block:
                unusable.update(block)
            else:
                self.__matrix = backup
                raise RuntimeError('cannot place boat')
        self.__available -= unusable

    def mark_cell(self, point, *, x_offset=0, y_offset=0):
        target = Point(point.x + x_offset, point.y + y_offset)
        if target in self.__available and \
                0 <= target.x < self.__width and \
                0 <= target.y < self.__height:
            self.__matrix[target.x][target.y] = True
            return {Point(target.x + xo, target.y + yo)
                    for xo in range(-1, 2)
                    for yo in range(-1, 2)}


if __name__ == '__main__':
    main()