Sonntag, 10. Februar 2013

Minelayer

Bei HP Codewars war 2010 als Problem 7 ein Minelayer zu coden. Ich habe damit jetzt einige Zeit verbracht das Problem zu verstehen und einen tüchtigen Ansatz zu entwickeln. Hauptproblem bei meinem Skript ist m. E. momentan die Funktion (minenfeld_zahlen_eintragen(felder)), die die Zahlen einträgt, weil ich es persönlich als umständlich wahrnehme, wie das codiert ist.

Ein großer Fortschritt gegenüber meinen bisherigen Ansätzen ist jedenfalls, dass das Spielfeld als eine Liste von Elementen codiert ist und erst in der Ausgabe als Feld (Höhe x Breite) erscheint. Die Repräsentation als Liste ist m. E. einfacher als eine Liste bestehend aus einzelnen Listen zu verwalten.

Hier das Skript:
import random

def minenfeld_erzeugen(breite,hoehe,minen):
    ''' Diese Funktion erzeugt das Spielfeld
    '''
    felder = minen * "*" + (breite * hoehe - minen) * "0"
    felder = [a for a in felder]
    random.shuffle(felder)
    return felder


def minenfeld_ausgeben(felder,breite):
    ''' Das Minenfeld ausgeben
    '''
    
    for position,feld in enumerate(felder,1):
        if feld == "0":
            print(".",end=",")
        else:
            print(feld,end=",")
        if position % breite == 0:
            print()


def minenfeld_zahlen_eintragen(felder):
    ''' Beispiel

    ....    ..11
    ...*    111*
    *...    *322
    *.*.    *3*1

    1 2 3
     \|/ 
    4-X-6
     /|\ 
    7 8 9
    
    '''
    for position,feld in enumerate(felder):

        if feld == "*":
            pass   # Da ist eine Mine drauf
        else:
            
            feld = int(feld)
            '''
            1 2 3
             \|/ 
            4-X-6
             /|\ 
            7 8 9
            '''

            positionen = []
            
            ''' Problem

            Im Augenblick werden Sachen fälschlich ausgewertet,
            m. E. gibt es 5 Sonderfälle, die abgefangen werden müssen

            B CCCC D
            
            A XXXX E
            A XXXX E

            '''
            
            # Fall B
            if position == 0:
                positionen = [position + 1,             # 6
                              position + breite,        # 8
                              position + breite + 1]    # 9
            # Fall A
            elif position % breite == 0:
                positionen = [position - breite,        # 2
                              position - breite + 1,    # 3
                              position + 1,             # 6
                              position + breite,        # 8
                              position + breite + 1]    # 9
            # Fall D
            elif position == breite -1:
                # Muss hier stehen, weil sonst die Bedingng
                # bei Fall C das hier 'überschreibt'
                # War zuvor Ursache für einen Bug
                positionen = [position - 1,             # 4
                              position + breite - 1,    # 7
                              position + breite,]       # 8
            # Fall C
            elif position < breite:
                positionen = [position - 1,             # 4
                              position + 1,             # 6
                              position + breite - 1,    # 7
                              position + breite,        # 8
                              position + breite + 1]    # 9
            # Fall E
            # Hier hatte ich Pkt. (%) vor Strich uebersehen
            elif (position + 1) % breite == 0:
                positionen = [position - breite - 1,    # 1
                              position - breite,        # 2
                              position - 1,             # 4
                              position + breite - 1,    # 7
                              position + breite]        # 8
            # Fall X
            else:
                positionen = [position - breite - 1,    # 1
                              position - breite,        # 2
                              position - breite + 1,    # 3
                              position - 1,             # 4
                              position + 1,             # 6
                              position + breite - 1,    # 7
                              position + breite,        # 8
                              position + breite + 1]    # 9

            for item in positionen:
                try:
                    # print(item,felder[item])
                    if felder[item] == "*":
                       feld += 1
                except IndexError:
                    pass
        
        felder[position] = str(feld)
        
    return felder


# Eigentlich

breite = 30
hoehe = 15
minen = 60

felder = minenfeld_erzeugen(breite,hoehe,minen)

felder = minenfeld_zahlen_eintragen(felder)

minenfeld_ausgeben(felder,breite)
Hauptprobleme in der Entwicklung waren:

  1. Die Abhandlung der verschiedenen Fälle war problematisch, weil ich die if-Bedingungen zunächst von A nach E aufgelistet habe und somit einzelne Bedingungen nicht geprüft wurden, weil eine Bedingung zuvor erfüllt war. Dies führte zu Fehlern beim Minen legen.
     
  2. Die verschiedenen Fälle zu umgehen ist mir nicht gelungen. Ich hätte gerne gehabt, dass die Fälle einfacher abzufangen gewesen wären, um mir unnötige Tipparbeit zu ersparen. Eine sinnvolle Lösung habe ich hier aber nicht gefunden.
     
  3. Eine objektorientierte Umsetzung drängt sich hier m. E. auf. Die drei Funktionen scheinen alle von einer anzulegenden Klasse Spielfeld abhängig.
     
Abschließend müsste eine Spielroutine implementiert werden, dass leere Felder und angrenzende Zahlen aufdeckt und so ein Spielen ermöglicht.

Nachtrag

Ich habe mir gerade die offizielle Musterlösung in Java angesehen, die von Don Brace stammt. Im entsprechenden Code-Abschnitt, der gewisse Ähnlichkeiten zu meinem Ansatz aufweist, schreibt er:
/*
  * This could be better optimized, but I leave it up to you :) */

Zumindest kommt er zum gleichen Ergebnis wie ich.

1 Kommentar:

  1. Ich finde ja sowohl die flache Liste als auch die vielen handgeschriebenen Sonderfälle nicht so schön. Ich hätte eine verschachtelte Liste genommen, eine Koordinaten-Klasse und immer alle 9 Möglichkeiten getestet. Man kann ja prüfen ob die Koordinate innerhalb des Spielfelds liegt oder nicht.

    AntwortenLöschen