using System; using System.Text; using Nemerle.Collections; namespace Gon { public enum Color { | Black | White | Empty } /// klasa goban - reprezentacja planszy do gry w go. /// /// metody tej klasy sprawdzają dostępność ruchu i sprzątają po nim. public class Goban { /// prywatna klasa stone - reprezentacja pojedynczego kamienia. private class Stone { public x: int; public y: int; public mutable group: Group; /// konstruktor klasy Stone. /// /// cx, cy to wspolrzedne na planszy (gorny prawy róg to 0,0), /// cgroup to grupa do której należy kamień. public this(cx:int, cy:int, cgroup:Group) { x = cx; y = cy; group = cgroup; } } /// prywanta klasa grupa - reprezentacja grupy polaczanych kamieni. /// /// klasa posiada swoj rozmiar, ilosc oddechow i kolor private class Group { public color : Color; public mutable stones : list [Stone]; public mutable counter : int; /// breaths public mutable size : int; /// number of stones; /// Dołącz grupę. /// /// Funkca Łączy podaną grupę z grupą dla której Concat zostało wywołane. /// Rozmiar grupy jest także zeminiany. public Concat(g: Group): void { foreach (p in g.stones) p.group = this; stones += g.stones; size += g.size; } /// Dodaje kamień. /// /// Funkcja Dodaje kamień do grupy i zmienia jej rozmiar. public Add(n: Stone): void { stones = n::stones; n.group = this; size++; } /// Stwórz grupe. /// /// Funkjca tworzy pustą grupe w podanym kolorze. public this(col: Color) { color = col; size = 0; stones = []; } } static directions : list [int * int] = [(0,-1), (1,0), (0,1), (-1,0)]; /// prywatna klasa Breaths - reprezentacja listy oddechów skojarzonej z każdym polem. class Breaths { public mutable places: array[2, list [Group]]; goban: Goban; /// Wyczyść tablice oddechów i rozmiary grup. /// /// Funkcja czuści tablice oddechów i ustawia rozmiary grup na 0. public CleanUp() : void { mutable y = 0; mutable x = 0; for (; y < goban.size; y++) for (x = 0; x < goban.size; x++) places[x,y] = []; foreach (p in goban.groups) p.counter = 0; foreach (p in goban.groups) { p.size = 0; foreach (_ in p.stones) p.size++; } } /// Przelicz oddechy. /// /// Funkcja tworzy listy grup skojrzonych z każdym polem na gobanie. public Count(): void { mutable x = 0; mutable y = 0; def TryAdd(ox, oy) { when (goban.area[x, y] == null && goban.Exists(ox, oy) && goban.area[ox, oy] != null) { def group = goban.area[ox, oy].group; when (places[x,y].Contains(group) == false) { places[x, y] ::= group; group.counter++; } } } CleanUp(); for (; y < goban.size; y++) for (x = 0; x < goban.size; x++) foreach ((ox, oy) in directions) TryAdd(x + ox, y +oy); } /// Konstruktor objektu Breaths. /// /// Funkcja bierze goban dla którego stworzony objekt, ma liczyć oddechy. public this(g: Goban) { goban = g; places = array(g.size, g.size); CleanUp(); } } /// rozmiar planszy. public size: int; /// para numer ruchu i pojedynczy zbity w nim kamień. mutable ko: (int * Stone); /// tablica kamieni. mutable area: array[2, Stone]; mutable they: list [Group]; /// lista grup mutable groups: list [Group]; /// lista więzniów mutable prisoners: list [Group]; /// lista ruchów mutable moves: list [(int*int*Color)]; /// numer ruchu mutable move_number : int; breaths: Breaths; /// Parametr uzywany przez funkcje Is* mutable x: int; /// Parametr uzywany przez funkcje Is* mutable y: int; /// Parametr uzywany przez funkcje Is* mutable color: Color; /// Sprawdź czy samobójstwo. /// /// Funkcja sprawdza, czy badany ruchjest samobójczy IsSuicide(): bool { mutable ret = true; def CheckOne(g : Group) { when ((g.color == color && g.counter > 1) || (g.color != color && g.counter <= 1)) ret = false; } def l = breaths.places[x, y]; when (l.IsEmpty) ret = false; foreach ((ox, oy) in directions) when (Exists(x + ox, y + oy) && !NotEmpty(x+ox, y+oy)) ret = false; when (ret) foreach (g in l) CheckOne(g); ret; } /// Sprawdź czy ko /// /// Funkcja sprawdza, czy w miejscu ruch nie występuje KO IsKo(): bool { /// check if not IsKo /// /// - remember from what pos was the last 1-elem. group beaten (if in previous turn) /// - if this (remembered) pos is beaten then ko occurs false; } /// Zabij podaną grupę. /// /// Funkcja zabija i sciąga podaną grupe z planszy. /// Martwe piony są dodawane do listy prisoners, ustawiane jest ko. Kill(g: Group): void { foreach (s in g.stones) area[s.x, s.y] = null; /// (14:15:malekith) groups = groups.Filter (fun (y) { ! y.Equals (g) }); /// (14:24:kamil-skalski) public Remove (x : 'a) : list['a] groups = groups.Remove(g); prisoners ::= g; when (g.size == 1) ko = (move_number, g.stones.Head); } /// Sprawdz czy x,y istnieje. /// /// Funkcja sprawdza, czy podany cx, cy nalezy do zakresy wielkości gobana. public Exists(cx : int, cy: int): bool { (cx >= 0 && cy >= 0 && cx < size && cy < size); } /// Sprawdz czy x,y jest pełne. /// /// Funkcja sprawdza czy w miejscu cx, cy stoi kamień. public NotEmpty(cx:int, cy:int) : bool { (Exists(cx, cy) && area[cx,cy] != null); } /// Sprawdź czy ruch jest dostępny. /// /// Funkcja sprawdza czy w miejscu cx, cy można postawić piona /// w kolorze col. public Avaible(cx:int, cy:int, col:Color) : bool { x = cx; y = cy; color = col; (Exists(x,y) && !NotEmpty(x, y) && !IsSuicide() && !IsKo()); } /// Postaw kamień. /// /// Funkcja próbuje postawić kamień, jeśli pole jest dostępne. /// Po postawieniu kamienia wykonywane są niezbędne czynności /// porządkujące plansze. public Put(cx: int, cy: int, col: Color) : void { mutable g = Group(col); def s = Stone(cx, cy, g); g.Add(s); breaths.Count(); def CheckOne(he) { if (he.group.color == col) { he.group.Concat(g); groups = groups.Remove(g); g = he.group; } else { when (they.Contains(he.group) == false) he.group.counter--; when (he.group.counter == 0) Kill(he.group); they ::= he.group; } } def Neighbour(ox, oy) { when (NotEmpty(cx + ox, cy + oy)) CheckOne(area[cx + ox, cy + oy]); } when (Avaible(cx, cy, col)) { they = []; foreach ((ox, oy) in directions) Neighbour(ox, oy); area[cx, cy] = s; when (groups.Contains(g) == false) groups ::= g; breaths.Count(); moves ::= (cx, cy, col); move_number++; foreach ((ox, oy) in directions) when (NotEmpty(cx + ox, cy + oy)) { def he = area[cx + ox, cy + oy]; when (he.group.color != col && he.group.counter <= 0) { Kill(he.group); Nemerle.IO.printf("killed %d\n", move_number); breaths.Count(); } } } } /// Zwróć tablice do narysowania. /// /// Funkcja zwraca tablice rozmiar*rozmiar typu kolor public State(): array[2, Color] { mutable ret = array(size, size); for (y = 0; y < size; y++) for (x = 0; x < size; x++) { ret[x,y] = Color.Empty; when (NotEmpty(x,y)) ret[x,y] = area[x,y].group.color; } ret; } /// konstruktor klasy Goban. /// /// Funkcja bierze rozmiar planszy. public this(s:int) { size = s; area = array(s,s); groups = []; prisoners = []; moves = []; move_number = 0; ko = (0, null); breaths = Breaths(this); } } }