Boter, kaas en eieren + N op een rij…

Deze pagina bevat uitvoerbare code.

In deze opgave schrijf je acht kleine functies, die allemaal erg op elkaar lijken, die 2D-lijsten in Python verwerken.

De applicatie die we in gedachten hebben is een spelbord waarvan je programma gaat bepalen:

  • Of er drie op een rij is voor een bepaald karakter (vier functies), en

  • Of er n op een rij is voor een bepaald karakter (vier functies die op de vorige vier lijken)

Gegeven code: een 2D-lijst afdrukken en aanmaken

Deze twee functies krijg je kant en klaar. Neem ze over in je eigen bestand en voer ze uit; je gebruikt create_array in alle tests hieronder.

# een functie om een 2D-lijst af te drukken
def print_2d(array):
    """Drukt de 2D-lijst array af, met een regel per rij.

    Argument: array, een 2D-lijst
    Resultaat: None (geen returnwaarde)
    """
    n_rows = len(array)
    n_cols = len(array[0])

    for row_ix in range(n_rows):  # n_rows == aantal rijen
        for col_ix in range(n_cols):  # n_cols == aantal kolommen
            print(array[row_ix][col_ix], end=" ")
        print()

    return None  # dit is impliciet aanwezig
    # als er geen return-statement aanwezig is


# een paar tests voor print_2d
array = [["X", " ", "O"], ["O", "X", "O"]]
print("2-row, 3-col array is")
print_2d(array)

array = [["X", "O"], [" ", "X"], ["O", "O"], ["O", "X"]]
print("4-row, 2-col array is")
print_2d(array)


# maak een 2D-lijst van een eendimensionale string
def create_array(n_rows, n_cols, s):
    """Geeft een 2D-lijst terug met n_rows rijen en n_cols kolommen.

    De gegevens komen uit de string s: eerst de eerste rij vol,
    dan de tweede, enzovoort. We testen alleen met genoeg gegevens!
    """
    array = []
    for _ in range(n_rows):
        new_row = []
        for _ in range(n_cols):
            new_row += [s[0]]  # voeg dat karakter toe
            s = s[1:]  # verwijder het eerste karakter
        array += [new_row]
    return array


# een paar tests voor create_array:
array = [["X", " ", "O"], ["O", "X", "O"]]
new_array = create_array(2, 3, "X OOXO")
assert new_array == array
print("Is new_array == array? moet True zijn:", new_array == array)

array = [["X", "O"], [" ", "X"], ["O", "O"], ["O", "X"]]
new_array = create_array(4, 2, "XO XOOOX")
assert new_array == array

Gegevensformaat

Merk op dat elke 2D-lijst in deze opgave rijen van losse karakters bevat:

  • array is de 2D-lijst als geheel

  • Elk element van array is een rij, en die rij is zelf een lijst

  • Elk element van een rij is een string van één karakter

  • Sterker nog, we beperken ons tot slechts drie strings:

    • 'X', een hoofdletter X,

    • 'O', een hoofdletter O,

    • en ' ', het spatieteken (dit is niet de lege string!).

Drie op een rij

De eerste vier functies die je gaat schrijven controleren of er drie op een rij is

  • in een specifieke richting (opgenomen in de functienaam),

  • voor een specifiek karakter char,

  • op een specifieke startrij- en kolom: row_start en col_start, en

  • in een gegeven 2D-lijst array.

Elk van de functies moet False teruggeven

  • als er GEEN RUIMTE is voor drie op een rij vanaf de startpositie gegeven door row_start en col_start (controleer dit eerst!), of

  • als row_start of col_start buiten de grenzen van array valt, of

  • (zelfs als er ruimte binnen de grenzen is), als er GEEN drie-op-een-rij-patroon binnen array is die helemaal bestaat uit het karakter char in de specifieke richting beginnend bij de locatie gegeven door row_start en col_start.

Elke functie moet daarentegen True teruggeven

  • alleen maar als er een drie-op-een-rij-patroon in array is die helemaal bestaat uit het element char in de specifieke richting beginnend bij de locatie van row_start en col_start.

Voorbeeld

Bekijk het volgende patroon, ontworpen voor de richting “oost”. Hier zie je een paar grenscontroles en een voorbeeld van een for-lus:

# voor de functie voor drie op een rij naar het oosten:

n_rows = len(array)  # aantal rijen is len(array)
n_cols = len(array[0])  # aantal kolommen is len(array[0])

if row_start >= n_rows:
    return False  # buiten de grenzen van de rijen

# andere grenscontroles...
if col_start > n_cols - 3:
    return False  # buiten de grenzen van de kolommen

# zijn alle gegevenselementen correct?
for ix in range(3):  # lusindex is ix
    if array[row_start][col_start + ix] != char:  # controleer op fouten
        return False  # fout gevonden; geef False terug

return True  # geen fouten gevonden in de lus; geef True terug

Merk op dat voor andere richtingen

  • Je andere controles nodig hebt (om te kijken of je niet uit de grenzen loopt).

  • Ook moet je de lus aanpassen voor andere richtingen

  • Het voorbeeld hierboven kijkt alleen naar drie op een rij in oostelijke richting.

Opdracht 1

Schrijf in_a_row_3_east(char, row_start, col_start, array). Deze moet bij row_start en col_start beginnen, kijken of er een drie op een rij van karakter char is in oostelijke richting en een toepasselijke True of False teruggeven.

Als je functie klopt, slagen deze tests zonder foutmelding:

# tests voor in_a_row_3_east
array = create_array(3, 4, "XXOXXXOOOOOO")
assert not in_a_row_3_east("X", 0, 0, array)
assert in_a_row_3_east("O", 2, 1, array)
assert not in_a_row_3_east("X", 2, 1, array)
assert not in_a_row_3_east("O", 2, 2, array)
# jouw oplossing


def in_a_row_3_east(char, row_start, col_start, array):
    """Geeft True terug als er vanaf (row_start, col_start) drie keer char naar het oosten staat."""
    ...

Opdracht 2

Schrijf in_a_row_3_south(char, row_start, col_start, array). Deze moet bij row_start en col_start beginnen, kijken of er een drie op een rij van karakter char is in zuidelijke richting en een toepasselijke True of False teruggeven.

Als je functie klopt, slagen deze tests zonder foutmelding:

# tests voor in_a_row_3_south
array = create_array(4, 4, "XXOXXXOXXOO OOOX")
assert in_a_row_3_south("X", 0, 0, array)
assert not in_a_row_3_south("O", 2, 2, array)
assert not in_a_row_3_south("X", 1, 3, array)
assert not in_a_row_3_south("O", 42, 42, array)
# jouw oplossing


def in_a_row_3_south(char, row_start, col_start, array):
    """Geeft True terug als er vanaf (row_start, col_start) drie keer char naar het zuiden staat."""
    ...

Opdracht 3

Schrijf in_a_row_3_southeast(char, row_start, col_start, array). Deze moet bij row_start en col_start beginnen, kijken of er een drie op een rij van karakter char is in zuidoostelijke richting en een toepasselijke True of False teruggeven.

Als je functie klopt, slagen deze tests zonder foutmelding:

# tests voor in_a_row_3_southeast
array = create_array(4, 4, "XOOXXXOXX XOOOOX")
assert in_a_row_3_southeast("X", 1, 1, array)
assert not in_a_row_3_southeast("X", 1, 0, array)
assert in_a_row_3_southeast("O", 0, 1, array)
assert not in_a_row_3_southeast("X", 2, 2, array)
# jouw oplossing


def in_a_row_3_southeast(char, row_start, col_start, array):
    """Geeft True terug als er vanaf (row_start, col_start) drie keer char naar het zuidoosten staat."""
    ...

Opdracht 4

Schrijf in_a_row_3_northeast(char, row_start, col_start, array). Deze moet bij row_start en col_start beginnen, kijken of er een drie op een rij van karakter char is in noordoostelijke richting en een toepasselijke True of False teruggeven.

Als je functie klopt, slagen deze tests zonder foutmelding:

# tests voor in_a_row_3_northeast
array = create_array(4, 4, "XOXXXXOXXOXOOOOX")
assert in_a_row_3_northeast("X", 2, 0, array)
assert in_a_row_3_northeast("O", 3, 0, array)
assert not in_a_row_3_northeast("O", 3, 1, array)
assert not in_a_row_3_northeast("X", 3, 3, array)
# jouw oplossing


def in_a_row_3_northeast(char, row_start, col_start, array):
    """Geeft True terug als er vanaf (row_start, col_start) drie keer char naar het noordoosten staat."""
    ...

Deze functies kunnen gecombineerd worden om elke drie op een rij op een spelbord te herkennen, bijvoorbeeld voor Boter, Kaas en Eieren. Je gaat hierna meer algemene versies hiervan maken…

Van 3 naar N: N op een rij

Leerdoel: je kunt een werkende oplossing generaliseren. Je hebt nu zelf vier functies geschreven die drie op een rij vinden. Hieronder staat wat de N-versies moeten doen, en verder niets: geen sjabloon en geen half ingevulde functie. Wat er verandert aan de grenscontrole, wat hetzelfde blijft en wat er met de vier richtingen gebeurt, zoek je zelf uit. Dat uitzoeken is de opdracht.

Boter, Kaas en Eieren is al lang opgelost! Laten we onbeperkt grote spelborden bekijken…

Om dit te doen, ga je je drie-op-een-rij-functies uitbreiden naar N-op-een-rij-functies.

Elke functie krijgt een extra argument aan het einde, een integer n, die het aantal identieke elementen (gelijk aan char) voorstelt die gevonden moeten worden om True terug te geven.

  • Als de positie buiten de grenzen valt; of binnen de grenzen, maar alsnog buiten de grenzen valt door de waarde van n, moet je functie False teruggeven.

  • Je functie moet natuurlijk ook False teruggeven als de rij wel binnen de grenzen valt, maar er geen N op een rij is!

Opdracht 5

Schrijf in_a_row_n_east(char, row_start, col_start, array, n). Deze moet bij row_start en col_start beginnen, kijken of er een N op een rij van karakter char is in oostelijke richting en een toepasselijke True of False teruggeven.

Als je functie klopt, slagen deze tests zonder foutmelding:

# tests voor in_a_row_n_east
array = create_array(5, 5, "XXOXXXOOOOOOXXXX XXXOOOOO")
assert in_a_row_n_east("O", 1, 1, array, 4)
assert in_a_row_n_east("O", 1, 3, array, 2)
assert not in_a_row_n_east("X", 3, 2, array, 4)
assert in_a_row_n_east("O", 4, 0, array, 5)
# jouw oplossing


def in_a_row_n_east(char, row_start, col_start, array, n):
    """Geeft True terug als er vanaf (row_start, col_start) n keer char naar het oosten staat."""
    ...

Opdracht 6

Schrijf in_a_row_n_south(char, row_start, col_start, array, n). Deze moet bij row_start en col_start beginnen, kijken of er een N op een rij van karakter char is in zuidelijke richting en een toepasselijke True of False teruggeven.

Als je functie klopt, slagen deze tests zonder foutmelding:

# tests voor in_a_row_n_south
array = create_array(5, 5, "XXOXXXOOOOOOXXXXOXXXOOOXO")
assert not in_a_row_n_south("X", 0, 0, array, 5)
assert in_a_row_n_south("O", 1, 1, array, 4)
assert not in_a_row_n_south("O", 0, 1, array, 6)
assert in_a_row_n_south("X", 4, 3, array, 1)
# jouw oplossing


def in_a_row_n_south(char, row_start, col_start, array, n):
    """Geeft True terug als er vanaf (row_start, col_start) n keer char naar het zuiden staat."""
    ...

Opdracht 7

Schrijf in_a_row_n_southeast(char, row_start, col_start, array, n). Deze moet bij row_start en col_start beginnen, kijken of er een N op een rij van karakter char is in zuidoostelijke richting en een toepasselijke True of False teruggeven.

Als je functie klopt, slagen deze tests zonder foutmelding:

# tests voor in_a_row_n_southeast
array = create_array(5, 5, "XOO XXXOXOOOXXXXOXXXOOOXX")
assert in_a_row_n_southeast("X", 1, 1, array, 4)
assert not in_a_row_n_southeast("O", 0, 1, array, 3)
assert in_a_row_n_southeast("O", 0, 1, array, 2)
assert not in_a_row_n_southeast("X", 3, 0, array, 2)
# jouw oplossing


def in_a_row_n_southeast(char, row_start, col_start, array, n):
    """Geeft True terug als er vanaf (row_start, col_start) n keer char naar het zuidoosten staat."""
    ...

Opdracht 8

Schrijf in_a_row_n_northeast(char, row_start, col_start, array, n). Deze moet bij row_start en col_start beginnen, kijken of er een N op een rij van karakter char is in noordoostelijke richting en een toepasselijke True of False teruggeven.

Als je functie klopt, slagen deze tests zonder foutmelding:

# tests voor in_a_row_n_northeast
array = create_array(5, 5, "XOO XXXOXOOOXOXXXOXXXOOXX")
assert in_a_row_n_northeast("X", 4, 0, array, 5)
assert in_a_row_n_northeast("O", 4, 1, array, 4)
assert not in_a_row_n_northeast("O", 2, 0, array, 2)
assert not in_a_row_n_northeast("X", 0, 3, array, 1)
# jouw oplossing


def in_a_row_n_northeast(char, row_start, col_start, array, n):
    """Geeft True terug als er vanaf (row_start, col_start) n keer char naar het noordoosten staat."""
    ...