Пытаюсь перенести мою интерактивную ncurses game of life с C на python

#python #c #curses #conways-game-of-life

#python #c #проклятия #конвей-игра жизни

Вопрос:

У меня единственная ошибка вне диапазона при попытке выполнить итерацию по сетке для подсчета соседних ячеек. Это почти сделано, но я уже потратил пару часов на эту ошибку. Я полон решимости разобраться с остальными из них, если таковые будут, после этого. лол. Я никогда раньше не обращался за помощью по stack exchange, поэтому будьте уверены, что я бы не стал, если бы не перепробовал все и не был полностью разочарован. Полегче со мной. лол

Код на C работает. Если кому-нибудь нужны объявления для реализации на C, я был бы рад их опубликовать.

Чтобы воспроизвести ошибку, запустите ее с помощью python3 в терминале * nix и нажмите «enter». Вы могли бы сделать это в Windows, но я думаю, вам нужно немного поработать с pdcurses.

Теперь кажется возможным, что в коде C есть необнаруженные ошибки, несмотря на то, что он работает и не выдает предупреждений компилятора.

Предполагается, что при выполнении итерации по 2D-массиву код должен работать путем добавления 1 к индексу сетки (grid.cell), чтобы не выходить за границы при подсчете ячеек непосредственно за пределами экрана. Из элементарного вывода отладки, который я добавил в grid.neighbour_count, я вижу, что он проходит весь путь до дальней стороны экрана по оси x, прежде чем выдает ошибку, поэтому отрицательное значение по оси y, похоже, работает так, как ожидалось. Я видел несколько других способов сделать это, но этот мне кажется самым элегантным, и он уже работает на C.

Сообщение об ошибке

 Traceback (most recent call last):
  File "./life.py", line 118, in <module>
    main()
  File "./life.py", line 100, in main
    grid.draw_next(term)
  File "./life.py", line 49, in draw_next
    j = self.neighbor_count(y,x)
  File "./life.py", line 38, in neighbor_count
    count  = self.cell[i 1][j 1]
IndexError: list index out of range
  

Python

 from blessed import Terminal
#import signal
import sys


def echo(text):
    print(text, end='', flush=True)

class Grid(object):
    def __init__(self, y, x):
        self.cell = [([0] * x) for i in range(y)]

    def resize(self, y, x):
        pass

    def width(self, x=0):
        if not x: return len(self.cell[0][:])

    def height(self, y=0):
        if not y: return len(self.cell[:][:])

    def toggle_cell(self, y, x):
        i = self.cell[y][x]
        if i:
            self.cell[y][x] = 0
            return 0
        else:
            self.cell[y][x] = 1
            return 1

    def neighbor_count(self, y, x):
        count = 0
        for i in range(y-1,y 2):
            for j in range(x-1,x 2):
# debug         sys.stderr.write('i: {0} j: {1} '.format(i, j))
# debug         sys.stderr.write('y: {0} x: {1}n'.format(y, x))
                count  = self.cell[i 1][j 1]
        return count - self.cell[y][x]


class Grid(Grid):
    lifeform = ' '

    def draw_next(self, term):
        new = [([0] * (term.width)) for i in range(term.height)]
        for y in range(term.height):
            for x in range(term.width):
                j = self.neighbor_count(y,x)
                if j == 2:
                    new[y 1][x 1] = self.cell[y 1][x 1]
                elif j == 3:
                    new[y 1][x 1] = 1;
                    with term.location(x=x, y=y):
                        echo(self.lifeform)
                else:
                    new[y 1][x 1] = 0
                    with term.location(x=x, y=y):
                        echo(' ')
        self.cell = new

def main():
    """Program entry point."""
    term = Terminal()
    y_pos,x_pos = 0,0

    def draw(y, x, string):
        with term.location(y=y, x=x):
            echo(string)
    def move(y, x):
        echo(term.move(y, x))

    grid = Grid(term.height, term.width)
    with term.cbreak(), term.fullscreen():
        inp, inp0 = '',''
        echo(term.clear)
        while inp0 not in ('q', 'Q', 'KEY_ESCAPE'):
            inp0 = term.inkey() # timeout=1
            inp = repr(inp0)
            if not inp:
                # timeout
                break
            elif inp0 == ' ':
                if grid.toggle_cell(y_pos, x_pos):
                    draw(y_pos, x_pos, grid.lifeform)
                else: draw(y_pos, x_pos, ' ')
            elif inp == 'KEY_UP' and y_pos > 0:
                y_pos -= 1
                move(y_pos, x_pos)
            elif inp == 'KEY_DOWN' and y_pos < term.height-1:
                y_pos  = 1
                move(y_pos, x_pos)
            elif inp == 'KEY_RIGHT' and x_pos < term.width-1:
                x_pos  = 1
                move(y_pos, x_pos)
            elif inp == 'KEY_LEFT' and x_pos > 0:
                x_pos -= 1
                move(y_pos, x_pos)
            elif inp == 'KEY_ENTER':
                grid.draw_next(term)
            # everything down here was for debugging info
            elif inp0 == 'x':
                draw(y_pos, x_pos, x_pos)
            elif inp0 == 'y':
                draw(y_pos, x_pos, y_pos)
            elif inp0 == 'w':
                draw(y_pos, x_pos, grid.width())
            elif inp0 == 'h':
                draw(y_pos, x_pos, grid.height())
            elif inp0 == 'W':
                draw(y_pos, x_pos, term.width)
            elif inp0 == 'H':
                draw(y_pos, x_pos, term.height)
            elif inp0 == 'g':
                draw(y_pos, x_pos, term.get_location())


if __name__ == '__main__':
    main()
  

C

 #include"life.h"

int main(){
    init_win();
    game_on = 1; //init_game();
    while(game_on){
        //get_color();
        input();
        wrefresh(stdscr);
    }
    quit();
    return 0;
}

void init_win(){
    initscr();
    nodelay(stdscr,TRUE);  // don't block for input, getch()
    raw();
    cbreak();           // take input chars one at a time, no wait for n
    noecho();           // don't print input directly
    curs_set(2);
    keypad(stdscr,TRUE);   // enable keyboard mapping
}

void init_game(){
    want_color = 1;

    srand(time(NULL));

    if (has_colors() amp;amp; want_color) { start_color(); }

    for (int i = 0; i < 8; i  )
        init_pair(i, color_table[i], COLOR_BLACK);

    game_on = 1;
}

void input(){
    switch(getch()){
        case 'q':
            nodelay(stdscr,FALSE);
            mvprintw(0,0,"Really quit? [y/N]");
            refresh();
            if(getch()=='y'){
                game_on = 0;
            }
            else{
                move(y_pos,x_pos);
                refresh();
                nodelay(stdscr,TRUE);
            }
        break;
        case 'c':
            clear_grid();
        break;
        case ' ':
            toggle_cell(y_pos,x_pos);
        break;
        case KEY_UP:
            if(y_pos > 0){
                y_pos--;
                move(y_pos,x_pos);
            }
        break;
        case KEY_DOWN:
            if(y_pos < LINES-1){
                y_pos  ;
                move(y_pos,x_pos);
            }
        break;
        case KEY_LEFT:
            if(x_pos > 0){
                x_pos--;
                move(y_pos,x_pos);
            }
        break;
        case KEY_RIGHT:
            if(x_pos < COLS-1){
                x_pos  ;
                move(y_pos,x_pos);
            }
        break;
        case 10: // 10 == newline
            // leaveok(stdscr,1);
            update_generation();
            move(y_pos,x_pos);
            //leaveok(stdscr,0);
        break;
    }
}

void clear_grid(){
    for (int y=0;y<1000;y  )
        for (int x=0;x<1000;x  )
            grid[y][x] = 0;
    erase();
}

void draw(int y,int x,char z){
    move(y,x);
    delch();
    insch(z);
}

void get_color(){
    chtype bold = (rand() % 2) ? A_BOLD : A_NORMAL;
    attrset(COLOR_PAIR(rand() % 8) | bold);
}

void toggle_cell(int y,int x){
    if (grid[y 1][x 1]==1){
        grid[y 1][x 1] = 0;
        draw(y,x,' ');
    } else {
        grid[y 1][x 1] = 1;
        draw(y,x,CELL);
    }
}

int neighbor_count(int y,int x){
    int count = 0;
    for(int i=y-1;i<=y 1;i  ){
        for(int j=x-1;j<=x 1;j  ){
            count  = grid[i 1][j 1];
        }
    }
    count -= grid[y 1][x 1];
    return(count);
}

void update_generation(){
    int new_grid[1000][1000];
    for (int y=0;y<LINES;y  )
        for (int x=0;x<COLS;x  )
            switch(neighbor_count(y,x)){
                case 2:
                    new_grid[y 1][x 1] = grid[y 1][x 1];
                break;
                case 3:
                    new_grid[y 1][x 1] = 1;
                    draw(y,x,CELL);
                break;
                default:
                    new_grid[y 1][x 1] = 0;
                    draw(y,x,' ');
                }
    for (int y=0;y<LINES;y  )
        for (int x=0;x<COLS;x  )
            grid[y 1][x 1] = new_grid[y 1][x 1];
}

void quit(){
    nocbreak();
    echo();
    endwin();
}
  

Комментарии:

1. Если вы находитесь на грани, x 1 и y 1 попытаетесь прочитать после конца списка (с края доски)… Я давно не писал на C, но я не думаю, что это явная ошибка / error / в C, скорее просто ошибка в вашей программе (если это имеет какой-то смысл). Но это ошибка в Python.

2. Почти уверен, что это grid = Grid(term.height, term.width) должно быть grid = Grid(term.height 2, term.width 2) . Основываясь на clear_grid функции, кажется, что вы решили проблему на C, сделав grid массив намного больше, чем необходимо.

3. Вау. Я знал, что это просто. Думаю, попытка использовать другую структуру и библиотеки немного сбила меня с толку. Я могу сделать массив немного больше с каждой стороны и считать ячейки только в пределах размера терминала. Я увеличил массив в C, чтобы очень просто изменить размер терминала и не иметь дела с выделением памяти, так что это была полная случайность, что все получилось таким образом. Я действительно ценю помощь ваших ребят. Рад, что я спросил. лол