LINUX.ORG.RU

python, практика, матрицы, крестики-нолики

 ,


0

1

Задача - проверить, кто выиграл в крестики-нолики.

Input: A game result as a list of strings
([
«X.O»,
«XX.»,
«XOO»]).
Output: «X», «O» or «D» as a string.

Решения в нете привязаны к полю 3х3. Пожелал сделать независимую проверку от поля. Код ужасен, прошу распотрошить, ткнуть носом в более изящный подход.

def checkio(result):
    length       = len(result)
    temp         = ""
    result_col   = []

    #check rows                                                                   
    for row in result:
        if row.count(row[0]) == length and row[0] != ".":
            return row[0]

    #check diagonal                                                               
    result_diag = ''.join(result[i][i] for i in range(length))
    if result_diag.count(result_diag[0]) == length and result_diag[0] != ".":
        return result_diag[0]

    #check diagonal reverse                                                       
    result_diag_rev = ''.join(result[length - 1 - i][i]
                                  for i in range(length - 1, -1, -1))
    if (result_diag_rev.count(result_diag_rev[0]) == length
                            and result_diag_rev[0] != "."):
        return result_diag_rev[0]

    #check columns
    for i in range(length):
        for j in range(length):
            temp += (result[j][i])
        result_col.append(temp)
        temp = ""

    for row in result_col:
        if row.count(row[0]) == length and row[0] != ".":
            return row[0]

    #anyway, draw                                                                 
    return "D"



Последнее исправление: masterdilly (всего исправлений: 1)
#[derive(Debug)]
struct InvalidInput;

fn foo(field: &[&str]) -> Result<char, InvalidInput> {
    let field_size = field.len();
    let valid_input = field.iter().all(|i|
        i.len() == field_size &&
        i.chars().all(|c| "XO.".chars().any(|x| x == c))
    );
    if !valid_input {
        return Err(InvalidInput);
    }
    
    let diagonals = [(0, field_size + 1), (field_size - 1, field_size - 1)];
    let isns = (0..field_size).map(|x| (x, field_size)).chain(
        (0..field_size).map(|x| (x * field_size, 1))
    ).chain(
        diagonals.iter().map(|x| x.clone())
    );
    
    for (init_skip, step_skip) in isns {
        let iter = (0..3).map(|x|
            field.iter()
                 .flat_map(|x| x.chars())
                 .nth(init_skip + x * step_skip)
                 .unwrap()
        );
        let folded = iter.fold('U', |acc, x| match (acc, x) {
            ('U', 'X') => 'X',
            ('U', 'O') => 'O',
            ('U', '.') => '.',
            ('X', 'X') => 'X',
            ('O', 'O') => 'O',
            _ => 'N'
        });
        println!();
        match folded {
            'X' => return Ok('X'),
            'O' => return Ok('O'),
            _ => continue
        }
    }
    Ok('D')
}

fn main() {
    println!("{:?}", foo(&["X.O", "XX.", "OOO"]));
}
anonymous
()

".join(result for i in range(length))

Не надо так делать. Лучше представить X и 0 в виде 0 и 1.

xpahos ★★★★★
()

Как сложно... Можно записать все символы в одномерный массив, все возможные строки по индексам - в другой массив (будет всего 8 значении), и этот другой пройти по очереди, создавая строку из символов первого массива. Если во время прохода получаем строку ХХХ или ООО - задача решена. Если все возможные строки прошли, а решения не нашли, значит, его и нет. Очень короткое решение.

anonymous
()
Ответ на: комментарий от anonymous

Ой, мимо, пропустил «Пожелал сделать независимую проверку от поля». Тогда советую сперва динамично создать второй массив, и только тогда сверять. :)

anonymous
()
Ответ на: комментарий от anonymous

Запихнуть всё в один? Можно это сделать за 1 проход? for в for? Потом склеить и после на проверку? Спасибо, попробую.

masterdilly
() автор топика

Как я помню на chekio можно смотреть рейтинговые решения: по хитрожопости, по краткости, по скорости итд.
И это офигенно в плане обучения.
Неужели ты думаешь тут тебе что-то лучше смогут предложить?

zolden ★★★★★
()
Ответ на: комментарий от zolden

В данном случае поле ограничено - 3х3. Все решения основаны на этом. Кто в кортежах все варианты прописал, кто просто

if row[0] == row[1] == row[2] and row[0] != ".":
перебирает. Я же хотел без привязки к полю.

masterdilly
() автор топика
def check(result):

    # horizontal
    h = filter(lambda x: x.count(x[0]) == len(x), result)
    if len(h):
        return(h[0][0] if h[0][0] != '.' else 'D')

    # vertical
    v = filter(lambda x: x.count(x[0]) == len(x),
               [map(lambda x: x[i], result) for i in range(len(result))])
    if len(v):
        return(v[0][0] if v[0][0] != '.' else 'D')

    # diagonal
    c = len(result[0]) // 2
    if result[c][c] in 'X0':
        d = filter(lambda x: x.count(result[c][c]) == len(x),
                   [''.join([x[i] for i, x in enumerate(result)]),
                    ''.join([x[i] for i, x in enumerate(reversed(result))])])
        if len(d):
            return(result[c][c])

    return('D')

vvn_black ★★★★★
()

Для проверки по диагонали я бы посчитал определитель матрицы. Если он не ноль, значит выиграл. :)

По вертикали и горизонтали уже попроще, тут наверное проще логикой. Не могу придумать как проверять математикой.

a1batross ★★★★★
()

у тебя очень много одинаковых проверок такого вида:

        if row.count(row[0]) == length and row[0] != ".":
            return row[0]

Выносим в функцию, получается (всё ещё не очень коротко, но уже становится виднее структура, куда двигаться дальше):

def who_win(s):
    if row[0] != '.' and row.strip(row[0]) == '':
        return row[0]

def checkio(result):
    length       = len(result)
    temp         = ""
    result_col   = []

    #check rows
    for row in result:
        won = who_win(row)
        if won:
            return won

    #check diagonal
    won = who_win(''.join(result[i][i] for i in range(length)))
    if won:
        return won

    #check diagonal reverse
    won = who_win(''.join(result[length - 1 - i][i]
                  for i in range(length - 1, -1, -1)))
    if won:
        return won

    #check columns
    for i in range(length):
        for j in range(length):
            temp += (result[j][i])
        result_col.append(temp)
        temp = ""

    for col in result_col:
        won = who_win(col)
        if won:
            return won

    #anyway, draw
    return "D"

Теперь внимательно смотрим на этот код. И становится понятно, что вместо кучи циклов с проверками можно сделать один, как-нибудь так:

def who_win(s):
    if s[0] != '.' and s.strip(s[0]) == '':
        return s[0]

def checkio(result):
    length = len(result)
    temp = ""

    # check rows
    to_check = result  # rows
    to_check.append(''.join(result[i][i] for i in range(length)))  # diagonal
    to_check.append(''.join(result[length - 1 - i][i]
                            for i in range(length - 1, -1, -1)))  # reverse diag
    # check columns
    for i in range(length):
        for j in range(length):
            temp += (result[j][i])
        to_check.append(temp)
        temp = ""

    for res in to_check:
        won = who_win(res)
        if won:
            return won

    # anyway, draw
    return "D"

Ну и дальше уже остаётся уже только по мелочи:

def who_win(s):
    if s[0] != '.' and s.strip(s[0]) == '':
        return s[0]

def checkio(result):
    length = len(result)

    # rows:
    to_check = result
    # diagonal:
    to_check.append(''.join(result[i][i] for i in range(length)))
    # reverse diagonal:
    to_check.append(''.join(result[i][-i-1] for i in range(length)))
    # collumns:
    to_check += [''.join(result[i][j] for i in range(length))
                         for j in range(length)]

    for res in to_check:
        won = who_win(res)
        if won:
            return won

    # anyway, draw
    return "D"

P. S. Это чисто куда можно двигаться от твоего готового кода, а не «идеальное» решение. Можно и с другой стороны зайти.

P. P. S. Да, в комментариях после решётки рекомендуется пробел всегда ставить (PEP8 рекомендует, и я здесь согласен, что так читаемее).

Psych218 ★★★★★
()
Последнее исправление: Psych218 (всего исправлений: 2)
Ответ на: комментарий от Psych218

Красота! Только не понятно, strip() лучше count()? И совсем не понятно по столбцам. вот этот участок: ".join(result[j] for i in range(length)) for j in range(length)].

как такой генератор читать?

И какие есть другие стороны? Вкратце?

masterdilly
() автор топика
Ответ на: комментарий от masterdilly

Только не понятно, strip() лучше count()?

Мне кажется более очевидным, потому что не требуется какой-то левый lenght.

И совсем не понятно по столбцам. вот этот участок: ".join(result[j] for i in range(length)) for j in range(length)].

Там result[j], а не result[j]. И вроде довольно очевидно же, даже не знаю, как объяснять, когда кажется и так понятным… Ну это в итоге становится

"".join(result[i][j] for i in range(3)) for j in range(3))
То есть:
"".join(result[i][j] for i in (0,1,2)) for j in (0,1,2))
то есть

"".join(result[i][0] for i in (0,1,2),
"".join(result[i][2] for i in (0,1,2),
"".join(result[i][3] for i in (0,1,2)

то есть

"".join((result[0][0], result[1][0] result[2][0])),
"".join((result[0][1], result[1][1] result[2][1])),
"".join((result[0][2], result[1][2] result[2][2]))

То есть, собственно, столбцами, представленными в виде строк. По сути то же, что и было у тебя, только без temp и генераторами (для меня, как уже для привыкшего к генераторам, так даже понятнее, на самом деле).

P.S. А, блин, я там с выравниванием напутал (хз, как вышло), это сбило с толку тебя, наверное. На работоспособность не влияет, но ухудшает читаемость. Должно быть так:

    to_check += [''.join(result[i][j] for i in range(length))
                 for j in range(length)]
Psych218 ★★★★★
()
Последнее исправление: Psych218 (всего исправлений: 4)
Ответ на: комментарий от Psych218

Расписано нормально, но я не понял. Вот генератор, как я знаю, за что отвечает та или та итерация? Вот который к концу ближе?

тоесть: [выражение итерация итерация]? самая правая - так называемый внешний цикл? И стремясь налево приближаемся к значению?

И хоть пару слов об альтернативных подходах к решению ;)

masterdilly
() автор топика
Ответ на: комментарий от masterdilly

Не могу понять, что именно тебе не понятно.

Всё это — один генератор. В качестве аргумента join — другой маленький генератор. Тебя это смущает.

Вот так по сути будет то же самое, может так понятнее:

    def collumn(j):
        return ''.join(result[i][j] for i in range(length))
    to_check += [collumn(j) for j in range(length)]

только эта функция collumn просто заинлайнена.

Ну да, правый получается внешний, потому что левый-то вообще внутри скобок у нас делается. Только генераторы это всё же не циклы.

А про альтернативные методы тут выше уже писали пару слов. Можно по-разному, можно всё в один список объединить, плоско сделать, например. Да и на этом checkio наверняка есть альтернативные варианты. Я бы, впрочем, наверное, писал как-то так же.

Psych218 ★★★★★
()
Ответ на: комментарий от Psych218

Повторюсь, большинство понятных решений написано с привязкой к 3х3 полю. Мне претит if == and == and == и т.д. Либо закапываются так, что я совсем не понимаю (а задача подразумевает прозрачное решение).
Мне нравится подход с итерацией. Марк Лутц писал, что это и есть особенность языка. Это не Си. Он на это упор делал. Примеров с генераторами мало. Может это связано с версиями Питона. Я не знаю.
В любом случае - спасибо Вам большое за потраченное на меня время. Ваш код понятен, а значит он высокого уровня, что и нужно для точки отсчёта в этом увлекательном мире программирования.

masterdilly
() автор топика
Ответ на: комментарий от Psych218

В качестве аргумента join — другой маленький генератор. Тебя это смущает.

Да. Это. Но я набью руку.

masterdilly
() автор топика

Парень, реально, завязывай с этой хероборой и иди на exercism.io, там тебе и код поревьювят и скажут почему говно написал и как переписать и вообще засрал ты тут всё

anonymous
()
16 мая 2018 г.
Ответ на: комментарий от anonymous

Довольно безобразен ваш коммент

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.