Uproszczenie i skrócenie kodu

Odpowiedz Nowy wątek
2018-12-04 14:05
0

Hej,
naszło mnie znowu na rozwijanie swojego programu więc... doszedłem do wniosku, że muszę wszystko przepisać.

Piszę komponent do grania w szachy. Doszedłem do momentu wyszukiwania posunięć, które można legalnie wykonać. Z napisaniem tego nie mam problemu, mam za to problem z DOBRYM napisaniem.

Załóżmy, że sprawdzam możliwe posunięcia dla wieży. Do tej pory miałem to tak rozwiązane, że sprawdzam możliwe ruchy w lewo, w prawo w górę i w dół - każdy kierunek w oddzielnej pętli. Przykład poniżej.
Niby działa ale dla wszystkich kierunków dla jednej tylko figury powstaje potworek na 100 linii.

Poniżej sprawdzenie tylko dla jednego kierunku ale całość wygląda analogicznie. Czy jest możliwość zamknięcie sprawdzenia wszystkich kierunków w jednej pętli albo ogranie tego w inny sposób? (Rekurencja? np z podaniem współrzędnych pola i kroku np x=0, y=-1 ?)

Bardzo proszę o naprowadzenie na rozwiązanie:)

Dodałem komentarze dla połapania się o co chodzi

procedure TBoard.RookMoves(field:TPoint); //dostaje X i Y w tablicy z szachownica
var
color:string;
i:integer;
begin
color:=Board[field.x,field.y].color; //kolor figury

for i:=field.x to 7 do
  begin
    if (Board[i,field.y].figure='') then //jezeli nic nie stoi na polu dodaje mozliwy ruch
    begin
      AddPossibleMove(Point(i,field.y));
    end
    else
    begin
        if (Board[i,field.y].color=color) then //jezeli moja figura, nie moge stanac
        begin
          Break;
        end
        else
        begin
          AddPossibleMove(Point(i,field.y)); //jezeli figura przeciwnika, moge zbic
          Break;
        end;
    end;
  end;

(...)

end;

Kto nigdy nie zrobił var dupa niech pierwszy rzuci kamień.
edytowany 2x, ostatnio: karpov, 2018-12-04 14:13

Pozostało 580 znaków

2018-12-04 14:44
2
karpov napisał(a):

Załóżmy, że sprawdzam możliwe posunięcia dla wieży. Do tej pory miałem to tak rozwiązane, że sprawdzam możliwe ruchy w lewo, w prawo w górę i w dół - każdy kierunek w oddzielnej pętli.

Wykorzystanie czterech pętli dla czterech możliwych kierunków ruchu nie jest dziwactwem. Problem pojawia się w przypadku Hetmana – tu mamy osiem kierunków. ;)

Skoro istnieje określona grupa bierek z takim samym stylem ruchu (np. Goniec, Hetman, Król – wszystkie po ukosie), to powinieneś mieć jedną metodę wyszukującą wszystkie możliwe pozycje docelowe. Oczywiście bierki te mają specyficzne cechy ruchu pomimo podobieństw – Król może przeskoczyć o jedno pole, a pozostałe o dowolną ich liczbę – więc jako dodatkowy parametr powinieneś okreslić maksymalną odległość.

Pamiętaj jednak, że liczba ruchów danej bierki wynosi 0 może być mniejsza niż maksymalna liczba ruchów (lub nawet równa 0), jeśli owa bierka jest związana. Czyli wtedy, gdy nie można swojej bierki ruszyć, bo bierka przeciwnika będzie szachować Króla. Dobrze by było taką informację przechowywać na bieżąco, zamiast ją określać przy/po każdym ruchu. Tak więc dodatkowe pole typu Boolean w klasie bierki rozwiąże sprawę.

Niby działa ale dla wszystkich kierunków dla jednej tylko figury powstaje potworek na 100 linii.

Unikniesz tego, jeśli pogrupujesz wspólne funkcjonalności do uniwersalnych metod, co pozwoli na bardzo krótkie i proste ich implementacje. W tym przypadku sugeruję skorzystać ze strategii bottom-up, dla ułatwienia pracy.

Poniżej sprawdzenie tylko dla jednego kierunku ale całość wygląda analogicznie. Czy jest możliwość zamknięcie sprawdzenia wszystkich kierunków w jednej pętli albo ogranie tego w inny sposób?

Raczej bym tego nie robił – lepiej mieć dużo małych i uniwersalnych metod, niż mniejszą ich liczbę, ale skomplikowanych i trudnych (lub wręcz niemożliwych) do ich wielokrotnego wykorzystania.

(Rekurencja? np z podaniem współrzędnych pola i kroku np x=0, y=-1 ?)

Rekurencja nie będzie tutaj potrzebna, bo niepotrzebnie zagmatwa kod i utrudni jego analizę. Mimo wszystko metoda sprawdzająca ruch danej bierki może przyjmować dwie wartości liczbowe, określające kierunek iteracji. Czyli wychodzi na to, że powinna przyjmować dwa parametry określające wektor ruchu oraz trzeci, dotyczący maksymalnej odległości – 1 (dla Króla lub Piona) lub np. Integer.High dla pozostałych bierek.

Absolutnym wyjątkiem jest Skoczek, którego ruchy są na tyle z d**y, że potrzeba dla niego osobnych metod. ;)


karpov napisał(a):
    if (Board[i,field.y].figure='') then //jezeli nic nie stoi na polu dodaje mozliwy ruch

Nie używaj ciągów znaków do określania typu bierki – użyj typu wyliczeniowego. Plansza gry powinna być macierzą referencji, do deklaracji której powinieneś wykorzystać bazową klasę bierek. Dobry powód do skorzystania z polimorfizmu.


edytowany 8x, ostatnio: furious programming, 2018-12-04 20:11
Trochę uprościłeś, bierka związana może pewne ruchy wykonywać. - bogdans 2018-12-04 17:17
Racja, byle w dalszym ciągu zasłaniała Króla. Poprawię post. - furious programming 2018-12-04 17:18

Pozostało 580 znaków

2018-12-04 15:07
0
furious programming napisał(a):

Problem pojawia się w przypadku Hetmana – tu mamy osiem kierunków. ;)

Pomyślałem, że skoro będę miał już funkcje poszukujące możliwych ruchów dla gońca i wieży to wykorzystam je do określenia ruchów hetmana, przekazując do nich jego pozycję.

Pamiętaj jednak, że liczba ruchów danej bierki wynosi 0, jeśli owa bierka jest związana. Czyli wtedy, gdy nie można swojej bierki >ruszyć, bo bierka przeciwnika będzie szachować Króla. Dobrze by było taką informację przechowywać na bieżąco, zamiast ją określać >przy/po każdym ruchu. Tak więc dodatkowe pole typu Boolean w klasie bierki rozwiąże sprawę.

Dokładnie tak miałem do tej pory to rozwiązane. Czyli dla każdego nowego 'możliwego' pola sprawdzałem czy ruch na to pole nie spowoduje odsłonięcia króla.
Czyli po wykonaniu ruchu powinienem sprawdzić i oznaczyć związane bierki?

Nie używaj ciągów znaków do określania typu bierki – użyj typu wyliczeniowego. Plansza gry powinna być macierzą referencji, do >deklaracji której powinieneś wykorzystać bazową klasę bierek. Dobry powód do skorzystania z polimorfizmu.

Zmodyfikuję kod i przypiszę każdej figurze określoną cyfrę i dla pustego pola dam np 0.
A co do skoczka... No cóż, na szczęście mam już to ograne w poprzedniej wersji ;)


Kto nigdy nie zrobił var dupa niech pierwszy rzuci kamień.

Pozostało 580 znaków

2018-12-04 15:13
karpov napisał(a):

Czyli po wykonaniu ruchu powinienem sprawdzić i oznaczyć związane bierki?

Sam bym tak zrobił, jednak najlepiej by było rozpisać sobie (nawet na kartce) wszystkie ważne rzeczy związane z ruchem bierek, tak by mieć pełen obraz sytuacji. Wtedy będzie można wybrać odpowiedni sposób przeprowadzania rozgrywki, tak aby nie zamordować procesora.

Zmodyfikuję kod i przypiszę każdej figurze określoną cyfrę i dla pustego pola dam np 0.

Nie robiłbym tak. Zwróć uwagę na to, że na danym polu dana bierka może stać lub nie. Sytuacja jest binarna, więc możesz sobie ułatwić robotę i zadeklarować planszę jako macierz referencji, gdzie pole puste zawiera nil, a pole zajęte zawiera referencję określonej bierki.

Po kliknięciu w dane pole należy przeliczyć koordynaty myszy na indeks pola, pobrać z macierzy referencję bierki i na podstawie jej typu odpalić metodę pobierającą wszystkie możliwe ruchy. Po wszystkim trzeba przepisać referencję z jednego pola macierzy do drugiego i wpisać nil do starego pola.

W przypadku bicia nic nie będziesz musiał robić ze zbitą bierką, a w przypadku zamiany Piona na Hetmana (po dotarciu na koniec planszy) wystarczy jedynie zmodyfikować enum bierki, okreslający jej typ. Roszada to też tylko przepisanie referencji, tyle że podwójne (jedno dla Wieży i jedno dla Króla).

Trudno o prostsze i bardziej zwięzłe rozwiązanie. ;)


edytowany 6x, ostatnio: furious programming, 2018-12-04 20:14

Pozostało 580 znaków

2018-12-04 15:17
0

najlepiej by było rozpisać sobie (nawet na kartce) wszystkie ważne rzeczy związane z ruchem bierek

Tam jest sporo zależności jak np roszada, bicie w przelocie itp. więc tak zrobię.

Po kliknięciu w dane pole należy przeliczyć koordynaty myszy na indeks pola, pobrać z macierzy referencję bierki, na podstawie jej typu odpal metodę pobierającą wszystkie możliwe ruchy. Po wszystkim przepisz referencję z jednego pola macierzy do drugiego i wpisz nil do starego pola.

To wszystko mam już zrobione oprócz właśnie określania poprawnych ruchów ale widzę, że muszę trochę przerobić kod :)

Ekstra, bardzo dziękuję Ci za pomoc :) Zawsze można na Ciebie liczyć.


Kto nigdy nie zrobił var dupa niech pierwszy rzuci kamień.
edytowany 3x, ostatnio: karpov, 2018-12-04 15:19
Nie ma sprawy. Tylko o Drag & Drop nie zapomnij. :P - furious programming 2018-12-04 15:21
Już zrobione :P - karpov 2018-12-04 15:26

Pozostało 580 znaków

2018-12-04 17:35
2

Oprócz aktualnej pozycji, musisz też częściowo pamiętać historię partii. Możliwość wykonania roszady i bicia w przelocie zależy od tego co się wydarzało w partii wcześniej.


To smutne, że głupcy są tak pewni siebie, a ludzie mądrzy - tak pełni wątpliwości. Bertrand Russell
Chcę dodać obsługę historii partii w formacie PGN. - karpov 2018-12-05 08:59

Pozostało 580 znaków

2018-12-04 20:16
0

@karpov: będziesz gdzieś publikował kod tego komponentu, czy jest to zamknięty projekt?


Pozostało 580 znaków

2018-12-05 08:57
2

@furious programming: Mam plan żeby go wrzucić tutaj do oceny jak już będzie skończony :)


Kto nigdy nie zrobił var dupa niech pierwszy rzuci kamień.

Pozostało 580 znaków

2018-12-05 13:41
0

No to fajnie – z chęcią się pobawię i sprawdzę kod. ;)

Źródła w ogóle będą dostępne, czy opublikujesz tylko apkę testową do przetestowania samego działania?


Pozostało 580 znaków

2018-12-05 14:25
0

Wrzucę apkę i źródła. Wrzucanie tylko apki byłoby obrazą dla Was po ogromnej pomocy jaką dostałem przy pracy nad tym (przez chyba już 3 rok ;) ).


Kto nigdy nie zrobił var dupa niech pierwszy rzuci kamień.
Ok, będę czekał. :] - furious programming 2018-12-05 14:51

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

1 użytkowników online, w tym zalogowanych: 0, gości: 1, botów: 0