2011. január 29., szombat

Új nevet kapott az ATI Stream SDK

Az OpenCL API-ra építő ATI Stream SDK 2009 végén látott napvilágot, és az elmúlt év nyarán kapott egy nagyobb frissítést is, melynek eredménye az OpenCL 1.1-es platform támogatása lett. A Stream SDK 2.3 az év elején jelent meg, de nem került ki hivatalosan az AMD weboldalára. Ennek most már az okát is tudjuk, ugyanis a fejlesztői csomag mostantól AMD APP SDK néven lesz elérhető.
A fejlesztőkörnyezet OpenCL-t érintő optimalizációkat tartalmaz, ám a legnagyobb újítás az UVD motor elérhetősége az OpenCL felületen keresztül. Ez a funkció egyelőre csak Windows 7 mellett él, de később más operációs rendszerekre is ki lehet terjeszteni. Az APP SDK természetesen támogatja az AMD új hardvereit, beleértve a Fusion APU-kat. Szintén érdekesség, hogy az AVIVO HD jelzést is eldobta cég. Korábban már beszámoltunk róla, hogy az ATI-tól megmaradt technológia már nem tökéletes az UVD motor képességeinek kihasználására, így a vállalat a driver újraírásán dolgozik. Az új meghajtó Media Codec Package néven fut, de a Catalyst 11.1-hez tervezett verzió, még számos ponton az AVIVO HD-ből megmaradt rutinokat kamatoztatja. A teljes áttérésre az év első felében érdemes számítani.
Az APP SDK 2.3 szintén támogatja a vállalat partnereinek már elérhető OpenPhysics SDK-t, mely egyelőre béta fázisban van, de később publikusan is elérhető lesz. A fejlesztői csomag a Pixelux DMM2 és a Bullett fizikai motorokhoz kínál támogatást. Az utóbbi felületet használja a 3DMark 11 is, mivel a rendszer képes némely fizikai számítás gyorsítására a DirectCompute API-n keresztül. A jövőt érintő fejlesztésekkel kapcsolatban megkerestük a Bullett vezető programozóját. Erwin Coumans elmondása szerint a motor új generációs verziója a márciusban megrendezendő, San Francisco-i GDC-n mutatkozik majd be, és a teljes futószalag gyorsítható a grafikus processzoron keresztül, az OpenCL vagy a DirectCompute felületet használva.
A AMD APP SDK 2.3 az alábbi oldalról tölthető le. A rendszer a Windows 7, Vista és XP operációs rendszerek mellett az openSUSE 11.3, a Red Hat Enterprise Linux 5.4/5.5, illetve az Ubuntu 9.10/10.04 verziójú Linux disztribúciókat támogatja. Ezenkívül kezeli a Microsoft Visual Studio (MSVS) 2008/2010 Professional Edition, a GCC (GNU Compiler Collection) 4.4-es, valamint az ICC (Intel C Compiler) 11-es fordítómodulokat.

2011. január 27., csütörtök

Építő (Builder)

Egy újabb olyan minta, ami a Factory-ból következik, ám attól picit eltérő a felhasználási módja. Az Építő (Builder) célja az, hogy a megfelelő elemeket menet közben építse fel különböző ábrázolási módszerekkel. Egy összetett objektum szerkezetének függetlenítése az ábrázolásától, így ugyanazzal az építési folyamattal különböző ábrázolásokat hozhatunk létre.

Motiváció

Egy RTF (Rich Text Format) dokumentumcsere formátum olvasónak képesnek kell lennie az RTF számos egyéb szövegformátumba való konvertálásra. Az olvasó átalakíthatja sima ASCII szöveggé vagy egy interaktívan szerkeszthető szövegeszközzé. A probléma mégis az, hogy a lehetséges konverziók száma végtelen. Így egyszerűen, az olvasó változtatása nélkül kell az új átalakítást megadni.
Egy megoldás, ha az RTFReader osztályt egy más szövegmegjelenítésre konvertáló TextConverter objektummal állítjuk be. Amikor az RTFReader elemzi az RTF dokumentumot, a TextConvertert használja az átalakítás végrehajtására. Amikor az RTFReader felismer egy RTF tokent (sima szöveget vagy RTF vezérlő szót), kiad egy kérelmet a TextConverter-nek, hogy alakítsa át az adott tokent. A TextConverter objektumok felelnek az adatkonverzióért és a token egy bizonyos megjelenítési formátumáért.
A TextConverter alosztályai specializálódhatnak különböző konverziókra és formátumokra. Például a az ASCIIConverter elutasíthat minden konverziós felkérést, kivéve a sima szövegeseket. A TeXConverter megvalósíthatja azokat a műveleteket, amelyek a TeX formátumban való, a szöveg minden stílusinformációját tartalmazó megjelenítésre vonatkozó kérelmekkel kapcsolatosak. A TextWidgetConverter olyan felhasználói felületet hozhat létre, amivel a felhasználó láthatja és szerkesztheti a szöveget.
Mindegyik konvertáló osztály-típus tartalmazza az összetett objektumok létrehozásához és összeállításához szükséges mechanizmusokat, és ezeket egy absztrakt interfész mögé rejti. Az átalakító független az olvasótól, amely az RTF dokumentum elemzéséért felelős.
A Builder minta magába foglalja mindezen kapcsolatokat. A mintában minden konverter osztályt builder-nek, és minden olvasót director-nak hívnak. A példát alkalmazva, a Builder minta elkülöníti a szöveges formátumot értelmező algoritmust (ez az, ami az RTF dokumentumokat elemezi) az átalakított formátum létrehozásától és megjelenítésétől. Lehetővé teszi számunkra, hogy az RTFReader elemző algoritmusát újra felhasználjuk különböző szövegmegjelenítés létrehozására RTF dokumentumokból – csak más TextConverter alosztályt kell beállítani az RTFReader számára.

Alkalmazhatóság

Akkor alkalmazzuk a Builder mintát, ha
  • Az összetett objektum létrehozási algoritmusának függetlennek kell lennie az objektumot felépítő részektől és azok összeállítási módjától.
  • A létrehozási folyamatnak lehetővé kell tennie a létrehozott objektum különböző megjelenítését.
Struktúra


Résztvevők

  • Builder (TextConverter) meghatározza a Product objektum létrehozására szolgáló absztrakt interfészt
  • ConcreteBuilder (ASCIIConverter, TeXConverter, TextWidgetConverter) a Builder interfészt megvalósításánál felépíti és összeállítja a termék részeit. Definiálja és nyomon követi az általa készített megjelenítési módokat. Interfészt biztosít a termék beolvasásához (pl.: GetASCIIText, GetTextWidget)
  • Director (RTFReader) a Builder interfész használatával létrehoz egy objektumot.
  • Product (ASCIIText, TeXText, TextWidget) a felépítendő összetett objektumot képviseli. ConcreteBuilder felépíti a termék belső ábrázolását és definiálja az összeállítási folyamatokat. Tartalmazza az alkotó elemeket defináló osztályokat, beleértve a részek összeállítására szolgáló interfészeket.

Együttműködési információk

  • A kliens létrehozza a Director objektumot és beállítja a kívánt Builder objektummal.
  • A Director értesíti a builder-t, ha egy objektum részét fel kell építeni.
  • A Builder kezeli a dircetor-tól érkező kérvényeket és a részeket ad a product-hoz.
  • A kliens elkéri a product-ot a builder-től.


A fenti diagram a Builder és a Director együttműködését szemlélteti a klienssel.

Következmények

A Builder minta föbb következményei:
  1. Lehetővé teszi a termék belső ábrázolásának a megváltoztatását. A Builder objektum a director számára egy absztrakt interfészt biztosít a termék felépítéséhez. Az interfész lehetővé teszi a builder-nek, hogy elrejtse a termék ábrázolását és belső felépítését. Elrejti a termék összeállításának módját is. Mivel a termék felépítése egy absztrakt interfészen keresztül történik, elég a termék belső ábrázolását megváltoztatni egy új builder típus definiálásához.
  2. Elszigeteli a felépítési és az összeállítási kódot. A Builder minta a modularitást az összetett objektum felépítésének és megjelenítésének az egységbe zárásával javítja. A kliensnek nem kell tudnia semmit a termék belső szerkezetét definiáló osztályokról; ilyen osztályok nem jelennek meg a Builder interfészében.
  3. Az építési folyamat finomabb vezérlését teszi lehetővé. Azoktól a létrehozási mintáktól eltérően, amelyek egy lépésben építik fel a terméket, a Builder minta lépésről lépésre építi fel a terméket a director felügyelete mellett. Csak akkor veszi át a director a terméket a builder-től, ha az elkészült vele. Emiatt a Builder interfész jobban tükrözi a termék felépítésének folyamatát, mint a többi létrehozási minta. Finomabb vezérlést tesz lehetővé az építési folyamatokon és ennek következtében a végső termék belső szerkezetén.

Implementációs rész

Tipikusan van egy absztrakt Builder osztály, ami minden egyes komponens működését definiálja, amelyek létrehozására a director megkéri. Alapértelmezésben a műveletek nem tesznek semmit. A ConcreteBuilder osztály felülbírálja azon komponensek műveleteit, amelyeknek létrehozásában érdekelt.
  1. Összeállítási és építési interfész. A Builder-ek termékeiket lépésről lépésre készítik el. Emiatt a Builder osztály interfésznek elég általánosnak kell lennie, hogy lehetővé tegye mindenfajta konkrét builder-nek a termékek létrehozását. A fő tervezési probléma az építési- és összeállítási folyamatok modellje. Azok a modellek megfelelőek, amelyek a létrehozási kérelmek eredményeit egyszerűen hozzácsatolják a termékhez.
  2. Miért nem absztrakt osztályokat használunk a termékeknél? Általában a konkrét builder-ek termékei annyira eltérnek a reprezentációjukban, hogy csak keveset nyerünk azzal, ha egy általános szülőosztályt adunk a különböző termékekhez. Mivel rendszerint a kliens a megfelelő konkrét builder-rel állítja be a director-t, ezért tudja, hogy a Builder melyik konkrét alosztálya van használatban, és ennek megfelelően kezeli a termékeket.
  3. Üres metódusok alapértelmezésként a Builderben. Üres metódusokat definiálunk, hogy a kliensek csak azokat a műveleteket bírálják felül, amelyekben érdekeltek.

Minta kód

A kódban szereplő osztályok:
  • Builder: VehicleBuilder
  • ConcreteBuilder: MotorCycleBuilder, CarBuilder, ScooterBuilder
  • Director: Shop
  • Product: Vehicle
Megvalósítás:
// Builder pattern -- Real World example

using System;
using System.Collections.Generic;

namespace Builder
{
///
/// Builder Design Pattern.
///

public class MainApp
{
///
/// Entry point into console application.
///

public static void Main()
{
VehicleBuilder builder;

// Create shop with vehicle builders
Shop shop = new Shop();

// Construct and display vehicles
builder = new ScooterBuilder();
shop.Construct(builder);
builder.Vehicle.Show();

builder = new CarBuilder();
shop.Construct(builder);
builder.Vehicle.Show();

builder = new MotorCycleBuilder();
shop.Construct(builder);
builder.Vehicle.Show();

// Wait for user
Console.ReadKey();
}
}

///
/// The 'Director' class
///

class Shop
{
// Builder uses a complex series of steps
public void Construct(VehicleBuilder vehicleBuilder)
{
vehicleBuilder.BuildFrame();
vehicleBuilder.BuildEngine();
vehicleBuilder.BuildWheels();
vehicleBuilder.BuildDoors();
}
}

///
/// The 'Builder' abstract class
///

abstract class VehicleBuilder
{
protected Vehicle vehicle;

// Gets vehicle instance
public Vehicle Vehicle
{
get { return vehicle; }
}

// Abstract build methods
public abstract void BuildFrame();
public abstract void BuildEngine();
public abstract void BuildWheels();
public abstract void BuildDoors();
}

///
/// The 'ConcreteBuilder1' class
///

class MotorCycleBuilder : VehicleBuilder
{
public MotorCycleBuilder()
{
vehicle = new Vehicle("MotorCycle");
}

public override void BuildFrame()
{
vehicle["frame"] = "MotorCycle Frame";
}

public override void BuildEngine()
{
vehicle["engine"] = "500 cc";
}

public override void BuildWheels()
{
vehicle["wheels"] = "2";
}

public override void BuildDoors()
{
vehicle["doors"] = "0";
}
}

///
/// The 'ConcreteBuilder2' class
///

class CarBuilder : VehicleBuilder
{
public CarBuilder()
{
vehicle = new Vehicle("Car");
}

public override void BuildFrame()
{
vehicle["frame"] = "Car Frame";
}

public override void BuildEngine()
{
vehicle["engine"] = "2500 cc";
}

public override void BuildWheels()
{
vehicle["wheels"] = "4";
}

public override void BuildDoors()
{
vehicle["doors"] = "4";
}
}

///
/// The 'ConcreteBuilder3' class
///

class ScooterBuilder : VehicleBuilder
{
public ScooterBuilder()
{
vehicle = new Vehicle("Scooter");
}

public override void BuildFrame()
{
vehicle["frame"] = "Scooter Frame";
}

public override void BuildEngine()
{
vehicle["engine"] = "50 cc";
}

public override void BuildWheels()
{
vehicle["wheels"] = "2";
}

public override void BuildDoors()
{
vehicle["doors"] = "0";
}
}

///
/// The 'Product' class
///

class Vehicle
{
private string _vehicleType;
private Dictionary _parts = new Dictionary();

// Constructor
public Vehicle(string vehicleType)
{
this._vehicleType = vehicleType;
}

// Indexer
public string this[string key]
{
get { return _parts[key]; }
set { _parts[key] = value; }
}

public void Show()
{
Console.WriteLine("\n---------------------------");
Console.WriteLine("Vehicle Type: {0}", _vehicleType);
Console.WriteLine(" Frame : {0}", _parts["frame"]);
Console.WriteLine(" Engine : {0}", _parts["engine"]);
Console.WriteLine(" #Wheels: {0}", _parts["wheels"]);
Console.WriteLine(" #Doors : {0}", _parts["doors"]);
}
}
}
Eredmény:


Ismert felhasználás

Különféle formátumok konvertálásához, fordítókban elemzőknél, normalizált adatbázisok „építéséhez”.

Kapcsolódó minták

Az Abstract Factory abban hasonlít a Builder-re, hogy az is összetett objektumokat építhet fel. Az elsődleges különbség, hogy a Builder minta az összetett objektumok lépésről lépésre való létrehozását helyezi középpontba. Az Abstract Factory esetében a termékobjektum-családok a hangsúlyosabbak (akár egyszerű, akár összetett). A Builder minta utolsó lépésben visszaadja a terméket, azonban az Abstract Factory minta, ami azt illeti azonnal.

Felhasznált anyagok

2011. január 23., vasárnap

The Secret of Monkey Island: Végigjátszás (IV.)

A Secret of Monkey Island anno forradalmasította a kalandjátékok műfaját, az egykori CoVboy-féle játékleírás pedig azóta is etalonnak számít a játékosok körében [hcgamer]. A régi pillanatok emlékére elhatároztam, hogy ezt a klasszikus írást a kornak megfelelően újraillusztrálom, és közreadom, hogy még nagyobb élményt okozzon az olvasása. Remélem nem haragszik meg érte CoVboy mester...

Negyedik rész, a kígyó a farkába harap

Az utolsó rész ott kezdődik, ahol az első véget ért: Mélée Island? mólóján. Guybrush egészen megkönnyebbült, hogy megszabadult szedettvedett társaságától. Aztán eszébe jut, hogy meg kellene állítani az esküvőt, még mielőtt nem késő és vidáman elszalad a templom felé. Egy másodperc múlva kevésbé vidáman vissza is szalad, mert egy szellem állja útját. Mindegy mit mondunk a szellemnek - mondjuk neki, hogy finom eladó szájvizünk van. A szellem csak akkor hajlandó venni, ha mentolos ízesítésű (csak azt szereti). Ennek örömére Guybrush jól lespricceli az antiszellem-létyóval. Egész jól működik... Szabad az út a templom felé. A következő sarkon egy másik szellem áll őrt - vele hosszas és elmés beszélgetést folytathatunk a szellemlét kérdéseiről (a végén úgyis lelocsoljuk), de rövidebb, ha a házon át levágjuk az utat.

A templomba pont a végszóra érkezünk. A padsorokban könnyező szellemek, a padsorok mögött szaladgáló lábak, az ifjú pár pedig az oltár előtt. Kiabáljunk közbe valami tartalmas dolgot, mire LeChuck azonnal hozzánk szalad, hogy már megint mit keresünk erre. Neki is bármit mondhatunk, aztán tátott szájjal bámulhatjuk, amint egy kötélen leereszkedik közénk a Kormányzó. LeChuck is kábé annyira meg van lepve, mint mi, a Kormányzó pedig nevetve meséli, hogy elég könnyű volt megszöknie, hiszen LeChuck legénységének a legtöbb tagja igen jó barátja volt, amikor még éltek. Nem túl sok értelme volt utánahajóznunk... Viszont ha a Kormányzó itt van, akkor ki van a mennyasszonyi ruhában? Egy kis meglepetés LeChucknak: két majom tűnik elő a fátyol alól és az egyik a kezében lóbálja az antiszellem-löttyöt. Elaine kormányzó ugyan szól, hogy ne riasszuk meg a majmokat, mert náluk van a szer, de Guybrush mégis elkergeti őket. Aztán ittmaradunk a derék szellemkalózzal kettesben...

LeChuckkal még tüzes szópárbajt vívhatunk, aminek mindenképpen az a vége, hogy egy hatalmas pofont mázol le nekünk. Guybrush eltűnik a légtérben, LeChuck meg utána. Szellemkalóz barátunk egy darabig azzal szórakozik, hogy végigver bennünket a sziget különböző pontjain, mígnem egy hatalmas maflástól a Stannél álló Grog-O-Matába zuhanunk. Stan boldogan siet üdvözlésünkre és a hajó után érdeklődik. Szívesen megvásárolná, ha esetleg el akarnánk adni... Közben megérkezik LeChuck is, akiben Stan új kuncsaftot vél felfedezni, és már kezdi is a mondókáját - amit sajnos a torkára forraszt egy hatalmas pofon. Miután Stan elrepült, LeChuck teljes figyelmét nekünk szenteli: hosszasan kutat utánunk a Grog-O-Matában, mígnem a pénzbedobónyíláson keresztül sikerül végre előhúznia bennünket. Amíg az újabb pofonhoz készülődik, felkaphatjuk a földről a szifont és lespriccelhetjük vele.


Az ördögi kalóz hirtelen atomjaira bomlik (azonnal ismétlést is kapunk róla) és látványos fényjelenségek közepette megindul az ég felé. Közben a Kormányzó is megérkezik hozzánk és együtt figyeljük a tűzijátékot az égen. "Hmmmm," - mormogja Guybrush, "hát lehet, hogy ez a LeChuck egy szörnyű fickó volt, de azért egy jót mégis elmondhatunk róla: igazán szép, ahogy ott durrog az égen..." Közben Stant is látjuk, amint földet (pontosabban vizet) ér az öböl közepén, majd a Monkey Island?-en ragadt Toothrot villan be a képbe, aki csodálkozik, hogy hová mehettek a többiek nélküle. Végül Guybrush levonja a - szintén választható - konzekvenciákat a játékkal kapcsolatban...

A készítők névsora is tartalmaz néhány igen nagy marhaságot, viszont a legvégére egy csodálatos életbölcsesség maradt: KAPCSOLD KI A SZÁMÍTÓGÉPET ÉS MENJ ALUDNI! Hát ennél többet már mi sem fűzhetünk ehhez a játékhoz...
CoVboy

2011. január 14., péntek

Gyártó függvény(Factory Method)

Az Elvont Gyár mintához szorosan kapcsolódik a Gyártó Függvény minta, amivel magát az Elvont Gyárat lehet létrehozni, de implementálhatóak Protoype használatával is. Tehát az előzőekben bemutatottakban felül itt egy rövid kód erről a mintáról:
class Creator where T : new()
{
T Create()
{
return Activator.CreateInstance();
}
}
Ennek lényege a megfelelő objektumok létrehozás anélkül, hogy annak részleteivel foglalkozni kellene egy magasabb absztrakciós szinten.

DXCoder

The Secret of Monkey Island: Végigjátszás (III.)

A Secret of Monkey Island anno forradalmasította a kalandjátékok műfaját, az egykori CoVboy-féle játékleírás pedig azóta is etalonnak számít a játékosok körében [hcgamer]. A régi pillanatok emlékére elhatároztam, hogy ezt a klasszikus írást a kornak megfelelően újraillusztrálom, és közreadom, hogy még nagyobb élményt okozzon az olvasása. Remélem nem haragszik meg érte CoVboy mester...

Harmadik rész, a Majom Sziget

A leszállás még mindig nem teljesen tökéletes: főhősünk már megint fejjel érkezett célba (a tengerpart homokjába) és a kilövés mintha lángra gyújtotta volna nadrágjának hátulsó részét. Míg mi a homokban tekergünk amatőr struccként, egy majom kerül elő valahonnan és érdeklődve figyeli, hogy mit művelünk. A látottakhoz mindössze annyit tud hozzáfűzni, hogy "ook! eek!" - aztán távozik a dzsungelbe. Nemsokára egy újabb emberszabású figura érkezik.

    Toothrot: Hi, Herman Toothrot vagyok. (válaszra vár, aztán folytatja:) Ne is zavartasd magad azzal, hogy visszaköszönsz. Már 20 éve várok arra, hogy civilizált emberrel beszélhessek. Egyébként javaslom, hogy oltsd el azt a tüzet a fenekeden, mert még valaki megsérül tőle...
Miután Toothrot is eltűnt a dzsungelben, másszunk elő a homokból. Egy hatalmas banánfa alatt landoltunk, amire valaki egy cédulát tűzött a következő szöveggel:
    "Rendkívüli népgyűlés szerda este! Meg kell vitatnunk, hogy az ördögi LeChuck szellemkalóz beköltözése a Majomfejbe milyen hatással van a környezetünkre és a turistaforgalomra! Monkey Island? összes kannibáljának megjelenésére számítunk!"
Hm. Érdekes. Ennél még érdekesebb, hogy a sekély vízben egy csónak áll, rajta Sea Monkey felirattal. Valószínűleg ezen szállt partra Toothrot és a kapitánya valamikor régen... A csónakot evezők híján egyelőre használni nem tudjuk. Bánatunkban vegyük fel azt a banánförtüt, ami a fa alatt fekszik (PICK UP BANANA) és sétáljunk be a dzsungelbe.

A sziget helyszínei közötti közlekedés hasonlít a Mélée Island?-en tapasztaltakhoz. Rögtön a tengerpart mellett rábukkanunk az előbbi majomra, amiről kiderül, hogy rendkívül éhes. "Mit eszik egy majom?" - tehetnénk fel a költői kérdést, de mivel tudjuk, inkább odaadjuk neki a hajózási szaktanácsadókat. Miután rájöttünk, hogy az nem ízlik neki, inkább banánt adunk neki. Azt villámgyorsan eltünteti, és érdeklődve figyeli a folytatást - sajnos az éhsége egy cseppet sem csillapult. Na nem baj, majd később felhizlaljuk. Továbbhaladva a sziget belseje felé egy szakadékba (CRACK) botlunk. A függőleges sziklafalon kissé körülményes lenne a lejutás, pedig ha egy kicsit vizsgálódunk, akkor a szakadék alján egy pár evezőt pillanthatunk meg. Talán némi kötélre volna szükségünk...

A szigetet egy kelet-nyugat irányba húzódó hegység szeli ketté, amely csak egyetlen helyen szakad meg: itt egy folyó folyik rajta keresztül. A folyó annak idején több ágból állt, de az egyik medre teljesen ki van száradva. Mihelyt elsétáltunk a folyóelágazáshoz (RIVER FORK), azt is megtudjuk miért: egy mesterséges gát zárja el. A folyón átívelő híd előtti sziklán egy újabb papírdarab található:

    "LeChuck szellemkalóznak.
    Ismételten fel kell kérnünk Önt, hogy korlátozza éjszakai tevékenységét a Szent Majomfej területén! Néhány ember ugyanis szeretne aludni! Próbálja csökkenteni a zajszintet!
    Monkey Island? kannibáljai
    U.I: Láttuk ám, amikor azt a kendős nőt levitte magával!"
Az írást eddig egy érdekes kődarab tartotta a helyén: egy kovakő, ami a levéllel együtt hozzánk került. A hídon keresztül átjuthatunk a függőleges sziklafalba vágott lépcsőhöz, ami a sziget legmagasabb pontjára vezet - ott egyelőre nincs dolgunk, kövessük inkább a kiszáradt folyómedret, ami egy kis tóba (POND) torkollik. Azaz torkollt valamikor, amikor még víz is volt benne. Tó helyett érdekes látványban lesz részünk: a fán átvetett kötélen egy felakasztott ember himbálózik és a kezében egy másik darab kötelet tart (meg a nyakán is, de arra valószínűleg nem vágyott). A nyakában függő kötél egy tuskóban végződik (mármint a másik oldalán), ami a kiszáradt mederben fekszik. Sajnos a hulla kezéből sehogy nem tudjuk leszedni a kötelet, ezért inkább elolvassuk a mellette levő MEMO-t, hátha attól okosabbak leszünk:
    "Monkey Island? kannibáljaihoz!
    Nem érdekel, hogy imádkoztok a Majomfej előtt, ami az otthonom és hadműveleteim titkos bázisaként szolgál, de tartózkodjatok azon szokásotoktól, hogy a piszkos bálványaitokat a verandám előtt hagyjátok. Továbbá legyetek szívesek nem belépni a Majomfejbe!
    LeChuck, sz.k."
Ettől ugyan nem lettünk okosabbak, viszont közben megint előbukkan a fák közül Toothrot, aki magyarázattal szolgál az akasztott ember ittlétére: ez az ő öreg barátja és útitársa, akit sajnálatos baleset ért. Mintha kissé le is fogyott volna az utóbbi időben. Hosszas és igen elmés társalgást folytathatunk vele, hogy hogyan is kerültek ide és mit csinálnak itt, továbbá megtudhatjuk, hogy Toothrot mester a vulkán tetején lévő erődben (FORT) rendezte be rezidenciáját.

Egyből el is látogathatunk oda (a sziget északnyugati csücskében fekszik). Itt rögtön több érdekes dolog is megüti a szemünket: elsősorban a sarokban heverő kötél (PICK UP ROPE). Másodsorban egy távcső (PICK UP SPYGLASS). Harmadsorban pedig az a rozsdás ágyú, ami valószínűleg már elég régóta használaton kívül van. Egy ágyú - de sehol egy fazék! Mivel ettől bosszús hangulatba kerülünk, úgy döntünk, hogy felborítjuk az ágyút (PUSH CANNON). Mihelyt összedől, megjelenik Toothrot is, aki először az után érdeklődik, hogy hova lett a távcsöve, aztán azon átkozódik, hogy mihelyt kiteszi innen a lábát, rögtön megjelenik valaki, aki összepiszkolja a padlóját... Ha beszélgetünk vele, akkor elmeséli a már százszor hallott történetet a csimpánzok vezette hajóról - ő küldte őket Mélée Island?-re, hogy segítséget hozzanak neki. A sziget lakosságáról annyit tud mondani, hogy ő az egyetlen civilizált ember a szigeten. Van még egy bennszülött törzs is, akik gyűjtögetnek és vadászgatnak (FEJvadászgatnak), de a kannibálok nem veszélyesek. Mindaddig, míg az ember kölcsön nem ad nekik valamit, ami például Toothrot banánszedőjével is történt. Toothrot meg addig nem adja vissza a kannibálok kulcsát a Majomfejhez, amíg vissza nem kapja az ő drága banánszedőjét...

Küldetésünkről elárulhatunk neki annyit, hogy azért érkeztünk ide, hogy megmentsünk valakit. Toothrot buzgón helyesel: ő az, akit meg kell menteni - de miért nem jöttünk egy kicsit korábban? Mivel szervezetünk esetleg megsínyli a hosszabb beszélgetést ezzel a figurával, inkább búcsúzzunk el tőle és fordítsuk a figyelmünket a földön heverő dolgokra: némi puskapor és egy ágyúgolyó kerül hozzánk (PICK UP GUNPOWDER és CANNONBAL). Ezzel a felszereléssel vissza is sétálhatunk a folyóelágazásig, ahol meg fogjuk szüntetni a gátat. Először is tegyük be a gát kövei közé a puskaport (USE GUNPOWDER WITH DAM). Most meg kellene gyújtani valamivel... Szerencsére van nálunk egy kovakő, amit valami keményebb tárgyon végighúzva elég nagy szikrát csiholhatunk. Az ágyúgolyó pedig elég kemény tárgy... (USE FLINT WITH CANNONBALL) Guybrush rémült kiáltással menekül a kitörő víz elől, de még idejében sikerül kimenekülnie a mederből. A víz pedig szépen feltölti a kiszáradt tavat a folyóág végén... Az akasztott embert tartó tuskót a víz felemelte és a hulla szépen leereszkedett. Undorunkat leküzdve szépen kihalászhatjuk a kezei közül a kötelet (PICK UP ROPE).

Most már két kötél boldog tulajdonosaként visszamehetünk a szakadékhoz, ahol a két kötelet a sziklafal két pontján USE-olva leeresz kedhetünk a szakadék fenekére és felvehetjük az evezőket (PICK UP OARS). Most, hogy már van evezőnk, rögtön vízitúrára is indulhatunk a parton álldogáló csónakkal (USE OARS WITH ROWBOAT). Csónakkal megközelíthető a sziget északi része, ahol a kannibálok faluja helyezkedik el. Odabent minden teljesen csendesnek tűnik - sehol egy teremtett lélek. A gyanakvó kalandor azonban végigvizsgálja az összes kunyhót. Az első üres. A második teljesen üres. A harmadik magasan van, de Guybrush fogadni merne rá, hogy üres. A negyedik a legüresebb kunyhó a világon. Az ötödikről annyit tudunk meg, hogy ha minden igaz, a szótárban az "üres" szó mellett ennek a kunyhónak a képe volt az illusztráció... Ezek után egészen felüdülésnek hat, hogy a hatodik kunyhó be van zárva. Egy tehát minden figyelmünket a hatalmas kőbálványra fordíthatjuk: Guybrush csapdától tartva nem akar bemenni - ellenben az előtte levő, gyümölcsáldozatot tartalmazó tálból szívesen elemel két banánt (PICK UP BANANA). Na, ezt elvisszük a mi derék, éhező majmunknak...

Azaz csak vinnénk: mihelyt kifelé indulnánk, három népviseletbe öltözött kannibál állja el az utat. "Mondd csak, barátom, az egy banán ott a zsebedben vagy ennyire örülsz, hogy láthatsz bennünket?" - érdeklődik az első. Az első két válaszra a bennszülöttek hagynak nekünk valami esélyt: ha adunk nekik valamit, amit felajánlhatnak a Nagy Majomfejnek, akkor hajlandóak bennünket meghagyni jelenlegi megfőzetlen állapotunkban. Meg lehet próbálni esetleg beugratni őket holmi hátuk mögött megjelenő háromfejű majomra hivatkozva (Look behind you...), de csak majdnem dőlnek be a trükknek. Pedig mihelyt kimondtuk, tényleg megjelenik a hátuk mögött egy háromfejű majom... A vége mindenképpen az, hogy bekísérnek bennünket a zárt kunyhóba. (Valószínűleg ott tárolják a nyers élelmiszert - CoVboy) Kívülről két lándzsát is az ajtónak támasztanak, hogy elejét vegyék az ajtónyitogatási kísérleteknek. Ez a kunyhó nem üres: itt van például többek között Toothrot banánszedője is, amit most magunkhoz veszünk (PICK UP BANANA PICKER). A földön egy koponya hever - ki tudja, hátha jó lesz még valamire! (PICK UP SKULL) Alatta egy meglazult padlódeszkát találunk, amelyet kinyitva már nyitva is áll a menekülés útja. Sajnos, van egy kis bökkenő is: a banánszedő nem fér be a nyíláson, tehát azt kénytelenek vagyunk itthagyni.

Míg a bennszülöttek a falu főterén vadul vitatkoznak Guybrush elkészítésének módozatain, a vacsora hátul szépen kioson faluból... (Aki akar egy kicsit szórakozni, az nekiállhat idegesíteni a bennszülötteket: menjünk vissza megint a faluba és fogassuk el magunkat. A főnök valószínűleg tehetséges mágus is: már előre tudta, hogy a banántolvaj visszatér a tett elkövetésének színhelyére - a mögötte álló pedig azt hajtogatja, hogy sokkal jobb lenne azonnal megenni bennünket. Rövid vita kezdődik Guybrush koleszteroltartalmáról, aztán megint bekísérnek a kunyhóba, de a biztonság kedvéért most már be is deszkázzák az ajtót. Harmadszorra már mint ügyes kis menekülőművészt köszöntenek bennünket és a kunyhó ajtajára hatalmas láncok is kerülnek...)

Csónakázzunk vissza a banánokkal a sziget déli részére, keressük meg a majmot és adjuk oda neki mind a kettőt. Ezeket is villámgyorsan elnyeli, de még mindig nem tűnik jóllakottnak a drága... Világos, hogy potenciális banánbeszerzőhelynek a parton álló banánfa számít - de arról talán csak a banánszedővel tudnánk szüretelni (az meg ott maradt a kannibálok kunyhójában). Így tehát a banánszedésnek egy teljesen más módozatát fogjuk választani.

Sétáljunk el a RIVER FORK-hoz, ahol a túlparton emelkedő sziklafalon egy lépcső vezet felfelé. A hatalmas szikla aljában valami egészen furcsa tákolmányt látunk: egy középen megtámasztott deszka egyik végén egy nagy szikla pihen - mintha valami katapult-szerűség lenne... Mellette természetesen egy MEMO-t találunk, rajta a következő szöveggel:

    "Monkey Island? kannibáljainak!
    Ne mozdítsátok el a szerkezetet. Pontosan be van állítva.
    Herman Toothrot"
Hm, hát ha az írás azt mondja, akkor lehet, hogy jobb békén hagyni. Másszunk tehát tovább felfelé a sziklafalon és egy lapos hegytetőre érünk, ahol a sziget csodálatos panorámája tárul elénk. A távolban az öbölben horgonyzó hajónkat látjuk (Ha nálunk van a távcső, akkor itt lehet használni: Mélée Island? kilátóját látjuk rajta, ahonnan az öregember egy másik távcsővel éppen minket néz...) Ha körbejárjuk a tetőt, akkor a jobb oldalán meglepő dolog történik: a föld hirtelen leomlik és Guybrush a szédítő magasságból lezuhan a dzsungelbe. Sokkal meglepőbb, hogy hirtelen a Sierra-játékokban megszokott RESTORE-RESTART-QUIT ablak jelenik meg, jelezvén, hogy a játék véget ért! Amíg ezen álmélkodunk, az antigravitáció szárnyain visszaröppen Guybrush is és közli, hogy épp egy gumifára esett... A tető szélén magányos kődarab álldogál és már puszta jelenlétével is arra ingerli az erre járókat, hogy lelökjék onnan (PUSH ROCK). Egészen véletlenül pont az egy szinttel lejjebb talált katapult másik végére pottyan. A katapulton levő szikla pedig engedelmeskedve a fizika törvényeinek megkezdi légiútját, és nemsokára célba is ér. Talán nem nehéz kitalálni, hogy mit találtunk el vele...

Természetesen a hajónkat, ami villámgyorsan el is süllyed. Így tehát mielőtt még felmásznánk ide, egy kicsit módosítunk a katapult irányzékán (kétszer PULL PRIMITIVE ART). Az így beállított katapulttal történő lövés pontosan a banánfát találja telibe! Miután visszamásztunk a katapulthoz, mindenki nagy örömére egy újabb MEMO tűnik fel:

    "Mr. Toothrot!
    Kérjük távolítsa el innen ezt a veszélyes szerkezetet! Citromfej véletlenül rápottyantott egy sziklát a felette levő szirtről és majdnem elsüllyesztett egy erre járó hajót!
    A kannibálok"
Sétáljunk ki a partra a banánfához, ahol az előbbi mesteri lövésnek köszönhetően két újabb banánt szedhetünk össze. Vigyük el ezeket is a majomnak, aki az ötödik banán elfogyasztása után már végre jóllakottnak érzi magát. Hálájáról úgy tesz tanúbizonyságot, hogy ezentúl a szárazföldön mindenhová követni fog bennünket. Ezt mondjuk jól teszi, mert most megyünk a sziget egyetlen helyszínére, ahol eddig még nem jártunk: a sziget nyugati részén levő tisztásra (CLEARING). Itt található az a bizonyos Szent Majomfej, amiről már annyi szó esett az eddigiekben. (Lehetőleg gyalog menjünk oda, hogy a majom követni tudjon bennünket).

A tisztás elején egy tábla fogad bennünket:

    "MAGÁNTERÜLET!
    Bálványimádni szabad, de a Majomfejbe belépni nem!
    LeChuck"
A tábla mögött néhány karóba húzott holttest és koponya teszi romantikussá a tájképet. (Nyilván ezek azok, akik nem olvasták el a tábla szövegét - CoVboy) Az ösvény végét egy bambuszkerítés zárja le, ami mögött a Majomfej látható. Ajtót sajnos sehol sem látunk... Persze ha egy kicsit gondosabban is elkezdünk vizsgálódni, akkor az ösvény mellett emelkedő két totemoszlop egyikén egy mozgatható kart fedezünk fel (PULL NOSE). Míg Guybrush lógázik a karon, egy ajtó nyílik meg a kerítésen - sajnos, mihelyt leugrunk, azonnal be is csapódik. A derék majom rögtön utánozni kezdi a gazdit és felugrik a karra. Mi meg nyugodtan besétálhatunk. A Majomfej előtti kert dugig van a kannibál folklór különböző bálványaival. Nyilván senkinek sem fog feltűnni, ha elvisszük a legkisebbet (PICK UP WIMPY LITTLE IDOL), amit egyébként a Citromfej néven közismert kannibál készített. Ennek szinte biztosan örülni fognak a kannibálok... Mivel a Majomfej zárva van, itt egyelőre úgy sem tudunk mit csinálni - irány tehát a kannibál falu.


Amikor a kannibálok lekapcsolnak bennünket, feleljük nekik az első vagy a második választ és amikor várják, hogy mivel akarjuk megváltani a szabadságunkat, adjuk oda nekik a kis bálványt (GIVE LITTLE WIMPY IDOL TO CANNIBALS).
    - Törzsfőnök: Hoppá! Hát ez igazán nagyszerű! Citromfej! Ezt nézd meg!
    - Citromfej (megvizsgálja): Hát ez tényleg nagyon szép - akárcsak az enyimek. És egyszerű - akárcsak az enyimek. És kicsi is - akárcsak az ennyimek. Nahát! Rá van írva, hogy Citromfej csinálta - akárcsak az enyimeken!
    - Törzsfőnök: Ezt már igazán elvihetjük a Majomnak. Köszönjük neked a nagyszerű ajándékot. Bármikor szükséged van bármire Monkey Island?-en, csak nyugodtan gyere el hozzánk...
Ezzel a törzs boldogan elvonul. A börtönnek kinevezett kunyhó ajtaja most nyitva van: most már elvihetjük a banánszedőt innen. A faluból kifelé menet Toothrotba botlunk, aki a bennszülöttek után kiabál és követeli vissza a banánszedőjét. Ha már annyira vágyik rá, akkor adjuk oda neki (GIVE BANANA PICKER TO TOOTHROT). Ennek igazán megörül. Nagy örömében odaadja nekünk a Majomfej kulcsát, hogy ha találkoznánk valamelyik bennszülöttel, akkor legyünk szívesek odaadni neki. Nem leszünk szívesek, mert nekünk is kell...

A szeleburdi játékos most hanyatt-homlok rohan a Majomhoz, hogy a kulccsal végre bejuthasson - aztán később majd meglepődik, amikor jól eltéved a labirintusban. A kannibálok felajánlották a segítségüket bármiben - miért ne használnánk ki? Tehát miután megkaptuk a kulcsot, menjünk ki a faluból és rögtön vissza. A három barátságos emberevő rögtön elősereglik fogadásunkra:

    - Kannibálok: Csak nem azért jöttél vissza, hogy lerójuk tartozásunkat a gyönyörű adományodért?
    - Guybrush: Hát éppenséggel volna valami...(Well, actually...)
    - Kannibálok: Mi az?
    - Guybrush: Tulajdonképpen keresek valakit.(I'm looking for somebody)
    - Kannibálok: Itt? Monkey Island?-en? Mi vagyunk az egyetlen nép, akik itt élünk? Az egyetlen civilizált nép.
    - Guybrush: Akkor helyben is vagyunk. Az emberek, akiket keresek, nem élnek.(That's okay...)
    - Kannibálok: Ja, hát AZOKRA a fickókra gondolsz? Már hónapok óta nyaggatnak bennünket: itt zörögnek körbe-körbe az idióta szellemhajóikkal... De mi már csak úgy vagyunk a kísértetekkel, hogy megfőzzük a mi kis varázsitalunkat és semmi félnivalónk tőlük.
    - Guybrush: Akkor miért nem csináljátok most is ezt?(So why don't...)
    - Kannibálok: A fő összetevője ennek a főzetnek egy nagyon ritka gyökér. Olyannyira, hogy csak egyetlen van belőle. Mindig csak egy kicsit szoktunk elhasználni, de most LeChuck ellopta tőlünk az egészet!
    - Guybrush: Hol rejtőzik?
    - Kannibálok: Van itt egy pokoli hely a sziget alatt, a katakombákon túl. A borzalmakat arrafelé minden magában hordozza... A turisták is órákig szoktak sorba állni, hogy láthassák.
    - Guybrush: Na és, mi történt? A KÖJÁL bezáratta?(What happened...)
    - Kannibálok: Nem, elvesztettük a kulcsot a Majomfejhez. Ellopták! Kölcsönadtuk Toothrotnak, de míg vissza nem hozza, addig a biztonság kedvéért magunknál tartottuk a banánszedőjét.
    - Guybrush: Elmegyek megkeresni LeChuckot és visszaszerzem nektek a gyökeret!(I'm off to find...)
    - Kannibálok: Hm, hát ez nem lesz túl könnyű. LeChuckot keresni nagyon veszélyes és sosem találod meg anélkül a... Hé!
    - Guybrush: Mi? Miről van szó?
    - Kannibálok: Navigatorszki fejecski nélkülecski.
    - Guybrush: Micski vancski azzalacski fejecskivel?(What's the...)
    - Kannibálok: A fenébe! Ez érti a mi bennszülött dialektusunkat! Hát annyit elmondhatok róla, hogy van egy fejünk, ami egy navigációs szerszám. Valamikor egy navigátoron volt... Mágikus úton életben tartottuk és útba igazít bennünket a katakombákban. Sajnos nem adhatjuk neked, mert csak egy van és az a mienk. Nem baj, cserébe azért, hogy nekünk adtad a bálványt, bármikor eljöhetsz és megnézheted a Majom kertjében.
Ha ismét beszélni akarunk velük, akkor is kitartanak elhatározásuk mellett: nem adják oda a fejet, mert nem tudják, hogy hogyan szerezzenek maguknak egy újat. A probléma megoldására a tudományos módszerrel jöttünk rá: megpróbáltuk odaadni nekik az összes tárgyat, ami nálunk volt. Aztán az egyik bejött (GIVE LEAFLET TO CANNIBALS). A főnök sajnos félreolvassa a címet és "Hogyan haladjunk előre..." helyett "Hogyan szerezzünk egy fejet..." olvas. Ettől rendkívül boldogok lesznek, mert így már odaadhatják nekünk és ők is szerezhetnek maguknak egy újat. A fejen még egy bonus nyaklánc is van, ami láthatatlanná tesz a szellemek előtt. A bennszülöttek ezzel búcsút is intenek: ha megvan a gyökér, várnak vissza szeretettel.


Menjünk vissza tehát a Majomfejhez. Mivel a kulcs úgy néz ki, mint egy hatalmas fülpiszkáló, logikus, hogy a Majom fülénél fogjuk USE-olni. Erre a majom eltátja a száját és egy lépcső jelenik meg benne. Odabent egyirányú az út egészen a katakombákig, ami egy elég kellemetlen útvesztő. Amikor beléptünk húzzuk elő a navigátor fejét (USE HEAD OF NAVIGATOR) és mindig pillantsunk rá: minden lépésünknél közölni fogja, hogy merre kellene mennünk (néha poénkodik és azt mondja, hogy egy kicsit megzavarodott). Így nemsokára elérjük a szellemhajót.

A fedélzetén 4 szellemkalóz mulatozik: hárman zenélgetnek, a kapitány csicskája pedig a fejét hajigálja ide-oda. Amikor a fedélzetre lépünk, azonnal odaszalad hozzánk. Mondhatunk valamit neki (kérdezzük meg mondjuk, hogy "Ez a Lido Fedélzet?"), de úgyis az a vége, hogy Guybrush ijedt sikollyal leszalad a hajóról. Elfelejtettük felvenni a navigátor nyakláncát, ami láthatatlanná tesz bennünket a szellemek előtt...

A navigátor nem nagyon akar megválni a láncától, de ha szépen kérjük (például "cukorral a tetején") vagy megígérjük neki, hogy bevágjuk a lávába, akkor hajlandó belátni, hogy nincs szüksége nyakláncra, amikor nincs is válla. A nyakláncot egyből vegyük is fel magunkra (USE NECKLACE ON NAVIGATOR) és így a hajón láthatatlan emberként fogunk ide-oda mászkálni.

A fedélzetről balra nyíló ajtó vezet LeChuck kabinjába. Barátunk szokás szerint az ablaknál bámul kifelé, de ha túl közel merészkednénk hozzá, azonnal visszakerget az ajtó elé. Pedig oly hívogatóan bámul ránk az a kulcs a falról!... Így tehát kénytelenek leszünk használatba venni a jó öreg Stan iránytűjét (USE MAGNETIC COMPASS WITH KEY). Az erős mágnes hatására a kulcs szépen belebeg a tárgyaink sorába.

A fedélzetről jobbra nyíló ajtó rettenetesen nyekereg és a csicska állandóan odaszalad becsukni - így tehát az utunk a fedélközbe vezet tovább. A legénységi szállásra érkezünk, ahol egy szellemkalóz hortyog részegen, de még álmában is vadul magához öleli a grogosbutykosát, ha valaki megközelíti.

A következő szobában néhány szellemcsirke és szellemmalac hizlalja magát, továbbá van itt egy erősen világító láda is, amelyben azt a bizonyos gyökeret tárolják. Sajnos a láda zárva van - csak valamilyen szerszám segítségével tudjuk kinyitni. Próbáljunk megfogni egy csirkét (PICK UP CHICKEN). Nem nagyon sikerült: mindössze egy tolla maradt a kezünkben. Nem baj, pont arra van szüksége az alvónak! Sétáljunk tehát vissza a szomszéd szobában hortyogó barátunkhoz és csiklandozzuk meg a talpát a csirketollal. Erre hatalmasat ugrik és kiejti a kezéből a grogosbutykost (PICK UP JUG O' GROG). Az állatkáktól egy csapóajtó is vezet tovább lefelé, de sajnos ez is zárva van. A zárjába véletlenül pont passzol a kapitánytól ellopott kulcs (USE KEY ON HATCH). A csapóajtó a raktárba vezet, ahol egy csomó patkány heverészik szanaszét, de mindannyian tökrészegek. Kivéve azt, amelyik vad morgással ránktámad, ha a raktár hátsó részei felé indulnánk. Természetesen őt is ugyanolyan állapotba ringatjuk, mint amilyenben a többiek leledzenek: öntsünk a tálkájába egy kis grogot (USE JUG O' GROG WITH ON DISH). A harcias patkány buzgón fellefetyeli az egészet és hamarosan ő is részegen hortyog a padlón. Mi pedig megnézhetjük mit rejt a hátul álló üst. Lisztet. Miután magunkhoz vettünk egy adagot belőle, sétáljunk vissza a fedélzetre, mert a nyikorgó ajtót bekenve vele, csöndben belopakodhatunk a dutyihoz. A cella előtt egy újabb szellemkalóz húzza a lóbőrt és Guybrushnak nem nagyon akarózik felébresztenie bármilyen antiszellem-kotyvalék nélkül. Egyébként is a falon lógó szerszámkészletért jöttünk (PICK UP GHOST TOOLS). A szerszámokkal menjünk vissza a gyökeret rejtő ládához és nyissuk ki velük (USE GHOST TOOLS WITH GLOWING CRATE). Ott mosolyog benne a gyökér, amiért idejöttünk...

Miután nálunk van a gyökér, itt is hagyhatjuk a szellemhajót. Hál'Istennek a játék készítői a visszautat a kannibálok falujába megspórolták nekünk, nem kell egy csomót szenvedni visszafelé... A bennszülöttek csodálkozva látják, hogy tényleg megszereztük a gyökeret. Rögtön szaladnak is megfőzni a varázsitalukat. Mi addig beszélgethetünk a háromfejű majommal, ami ismét feltűnik a kunyhók közül. A majom ugyan nem válaszol, viszont buzgón elrágcsál egy banánt. Ennyi idő alatt elkészül a főzet is, amit a kannibálok egy szódásszifonban nyújtanak át. Használati utasítás: "Csak spricceld rá egy szellemre, aztán majd meglátod. Ha megmaradna valamennyi a löttyből, akkor vaníliafagylaltként is fogyasztható..." Mire visszaérünk a titkos rejtekhelyhez, LeChuck hajója eltűnik! Mindössze a csicskása szomorkodik a parton.

    - Guybrush: Hé!
    - Csicska: Hé! Majdnem halálra rémítettél...
    - Guybrush: Hová lett a hajó?(What happened...)
    - Csicska: Mindenki elment az esküvőre.
    - Guybrush: Miféle esküvőre?!(What wedding?)
    - Csicska: LeChuck feleségül veszi Mélée Island? kormányzóját.
    - Guybrush: MICSODA?! Hogy tehetik ezt velem? Hol lesz az esküvő?(Where is the wedding?)
    - Csicska: Abban a szép kis templomban Mélée Island?-en.
    - Guybrush: Óh, nem! Utánuk kell mennem és meg kell állítanom őket.(I've got to go...)
Hirtelen berohan a Sea Monkey teljes legénysége és szemrehányóan veszik észre, hogy itt vagyunk. Azt mondják, hogy már nagyon régóta keresnek bennünket - aztán felismerik LeChuck csicskáját és vidám társalgásba bonyolódnak a régi szép időkről, a haláláról és hasonló témákról. Talán nem zavarja őket, ha mi is megjegyezzük, hogy vissza kellene mennünk Mélée Island?-re. "Hát persze" - mondja Carla. "Kifogytunk a napolajból..." - és ezzel a csatakiáltással mindannyian kiszaladnak a barlangból. Vége a III. résznek.
CoVBoy

Elvont Gyár (Abstract Factory)

A Gyár (Factory) célja, hogy objektumokat gyártson úgy, hogy a gyártás részleteivel a hívónak nem kell foglalkoznia. Az Elvont gyár (Abstract Factory) pedig egy felületet biztosít a gyárak számára a konkrét osztályok megadása nélkül, biztosítva így a továbbfejlesztési lehetőséget.

Alkalmazhatóság

Akkor használjuk az Abstract Factory mintát, ha
  • A rendszernek függetlennek kell lenni attól, hogyan hozza létre, állítja össze és jeleníti meg a termékeit.
  • A rendszernek be kell állítania egyet a termékcsalád közül.
  • Kapcsolódó termékobjektumok egy családját együttes használatra tervezték, és ezt a megszorítást ki kell kényszeríteni.
  • Gondoskodni kell a termékek osztálykönyvtáráról, és csak az interfészüket fedhetjük fel, de a megvalósításukat nem.
Struktúra


Résztvevők
  • AbstractFactory: az absztrakt termékobjektumokat létrehozó interfészt deklarálja.
  • ConcreteFactory: a konkrét termékobjektumok létrehozására szolgáló műveleteket implementálja.
  • AbstractProduct: a termékobjektumok egy típusának interfészt deklarálja.
  • ConcreteProduct: definiálja a létrehozandó termékobjektumot a megfelelő konkrét factory (gyár) mellett, implementálja az AbstractProduct interfészt.
  • Client: csak az AbstractFactory és az AbstractProduct osztályok által deklarált interfészeket használja.
Együttműködési információk

Normál esetben a ConcreteFactory osztály egyetlen példányát hozza létre futásidőben. Ez a konkrét factory egyéni implementációval rendelkező termékobjektumokat készít. Különböző termékobjektumok létrehozásához a kliensnek különböző konkrét factory-kat kell használnia. Az AbstractFactory a termékobjektumok létrehozását a ConcreteFactory alosztályra bízza.

Következmények

Az Abstract Factory mintának a következő előnyei és hátrányai vannak:
  1. Elszigeteli a konkrét osztályokat. Az Abstract Factory minta segít az alkalmazások által létrehozott objektumosztályok irányítását. Mivel a factory egységbe zárja a termékobjektum létrehozásának felelősségét és folyamatát, így elszigeteli a klienst az implementációs osztályoktól. A kliensek a példányokat saját absztrakt interfészükön át kezelik. A termékosztályok nevei elszigeteltek a konkrét factory implementációjában; azok nem jelennek meg a kliens kódjában.
  2. Megkönnyíti a termékcsaládok cseréjét. A konkrét factory osztálya egy alkalmazásban egyszer jelenik meg – ahol példányosítottuk. Ez megkönnyíti az alkalmazás által használt konkrét factory megváltoztatását. A különböző termék használatához csak a konkrét factory-t kell megváltoztatni. Mivel az absztrakt factory komplett termékcsaládokat készít, egyszere megváltozik az egész termékcsalád.
  3. Támogatja a termékek közti konzisztenciát. Amikor egy családon belül a termékobjektumokat együttműködésre tervezték, akkor fontos, hogy az alkalmazás egy időben csak egy családhoz tartozó objektumokat használjon. Az AbstractFactory ezt könnyen érvényesíti.
  4. Az új termékfajták támogatása nehézkes. Az absztrakt factory kibővítése újfajta termékek létrehozására nem könnyű. Azért mert az AbstractFactory interfész rögzíti a létrehozható termékek halmazát. Az újfajta termékek támogatásához ki kell bővíteni a factory interfészt, amihez meg kell változtatni az AbstractFactory osztályt és annak minden alosztályát.
Implementációs rész
  1. Factory-k mint Singleton-ok. Egy alkalmazásnak tipikusan csak egyetlen egy ConcreteFactroy példányra van szüksége termékcsaládonként. Így azokat általában legjobb Singleton segítségével implementálni.
  2. A termékek létrehozása. Az AbstractFactory csak a termékek létrehozására szolgálóinterfészt deklarálja. A ConcreteProduct alosztály végzi valójában a létrehozást. A legáltalánosabb megoldás, ha minden termékhez deklarálunk egy factory metódust. Amíg az implementáció egyszerű, addig minden egyes termékcsaládhoz szükség van egy konkrét factory alosztályra, még akkor is ha a termékcsaládok csak alig térnek el.
  3. Ha sok termékcsaládra van szükség, a konkrét factory implementálható a Prototype minta felhasználásával. A konkrét factory a családba tartozó minden termékhez külön tartozó prototípus példány segítségével inicializálható, és a prototípus klónozásával hozható létre új termék. A prototípus alapú megközelítés kiküszöböli, hogy minden egyes új termékcsaládnak új konkrét factory-ra lenne szüksége.
  4. ővíthető factory-k definiálása. Az AbstractFactory általában különböző műveleteket definiál az általa létrehozható minden termékfajtához. A termékek típusa a műveletek szignatúrájába van kódolva. Egy új terméktípus hozzáadása estén szükség van az AbstractFactory interfész és az összes tőle függő osztály megváltoztatására.
  5. Egy rugalmasabb, de kevésbé biztonságos kivitelezés, ha az objektumokat létrehozó műveleteket paraméterezzük. Ez a paraméter határozza meg a létrehozandó objektum típusát. Ez lehet egy osztályazonosító, egy egész szám, egy sztring, vagy bármi ami azonosíthatja a termék típusát. Ezzel a megközelítéssel, az AbstractFactory-nak csak egy egyszerű „Make” műveletre van szüksége, ami a létrehozandó objektum típusát jelző paraméterrel rendelkezik. Ezt a technikát használjuk a fent megbeszélt Prototype- és osztály-alapú absztrakt factory-kban.
Példakód

Az elvont gyár minta segítségével most létrehozunk egy pályaszerkesztőt DXCoder példája alapján. Alaposztályok a megjelenítéshez:
class Sprite 
{
public Sprite(int size, string textureName) { }
}

static class SpriteFactory
{
public static Sprite CreateBall()
{
return new Sprite(20, "ball.png");
}

public static Sprite CreateWall()
{
return new Sprite(100, "wall.jpg");
}
}

class Game
{
Sprite[,] map = new Sprite[10, 10];

public void CreateLevel()
{
map[0, 0] = SpriteFactory.CreateWall();
map[1, 0] = SpriteFactory.CreateWall();
map[1, 1] = SpriteFactory.CreateBall();
}
}
Mint látható a Game osztályban már nem kell foglalkozni az egyes elemek részleteivel. A CreateLevel helyére el lehet képzelni egy olyan ciklus, ami egy file-ból tölti be a pályát. A másik trükk, hogy felhasználtuk az Egyke mintát is egyúttal.
Mi van akkor, ha a pályaszerkesztővel több pályát is szeretnénk készíteni egységes jelöléssel? A wall az mindig W, a ball az pedig mindig B. Többféle világ is van, ahol a ball az lehet forest, vagy robo is. Nem szeretnénk átírni se az editort, se a pálya betöltőt, úgyhogy a gyárak körül kell változtatni. A következő példa már az Elvont gyárat mutatja be:
  • AbstractFactory: IAbstractFactory
  • ConcreteFactory: RobotSpriteFactory, ForestSpriteFactory
  • Client: Game (CreateLevel)
Forrás:
using System;

namespace ConsoleApp
{
static class SingletonManager where T : new()
{
static T instance = Activator.CreateInstance();

public static T Instance
{
get { return instance; }
}
}

class Sprite
{
public Sprite(int size, string textureName) { }
}

interface IAbstractFactory
{
Sprite CreateBall();
Sprite CreateWall();
}

class ForestSpriteFactory : IAbstractFactory
{
public Sprite CreateBall()
{
return new Sprite(20, "ball.png");
}

public Sprite CreateWall()
{
return new Sprite(100, "wall.jpg");
}
}

class RobotSpriteFactory : IAbstractFactory
{
public Sprite CreateBall()
{
return new Sprite(26, "roboBall.png");
}

public Sprite CreateWall()
{
return new Sprite(120, "roboWall.jpg");
}
}

class Game
{
Sprite[,] map = new Sprite[10, 10];

public void CreateLevel(IAbstractFactory factory)
{
map[0, 0] = factory.CreateWall();
map[1, 0] = factory.CreateWall();
map[1, 1] = factory.CreateBall();
}
}

static class Program
{
static void Main()
{
var forestGame = new Game();
forestGame.CreateLevel(SingletonManager.Instance);

var robotGame = new Game();
robotGame.CreateLevel(SingletonManager.Instance);
}
}
}
Látható, hogy a CreateLevel lényegében semmit nem változott. Ha több 100 elemet kell betölteni, akkor se történik változás, mindig a világnak megfelelő objektumok fognak betöltődni. Itt még pluszban az Egyke minta egyik C# változatát is felhasználtuk, hogy ne kelljen állandóan fölösleges objektumokat létrehozni.

Kapcsolódó minták

Az Abstact Factory osztályokat gyakran factory metódusokkal (gyártó függvényekkel) valósítják meg, de implementálhatóak Protoype használatával is.

Felhasznált anyagok

Ünnepel az Nvidia: elkészült az egymilliárdodik GeForce chip

[PC Fórum]: Az Nvidia csütörtökön jelentette be, hogy jelentős jubileumhoz ért: a cég által megbízott TSMC gyártósorairól ugyanis "legördült" az egymilliárdodik darab GeForce chip is. Az 1999 óta gyártott lapkák a legelterjedtebb és legismertebb grafikus áramköröket képezik az ATI/AMD Radeon és az Intel integrált chipkészletei mellett.
A sorozat első tagját a tizenkét évvel ezelőtt bemutatott GeForce 256 képezte. A belsőleg NV10 kódnév alatt fejlesztett chip ugyan csak 120 MHz-en futott és alig 4 darab pixelfeldolgozósorral rendelkezett, mégis addig elképzelhetetlen grafikus teljesítményt hozott el a PC-s platformra. Ez utóbbinak köszönhetően az Nvidia az ismeretlenségből kitörve néhány éven belül a vezető grafikus chipgyártóvá nőtte ki magát, lenyomva konkurenseit, sőt, egyenesen csődbe kergetve az addig a piacot uraló 3dfx-et, amelynek maradványait végül fel is vásárolta.


Az évek során aztán persze az Nvidia újabb és újabb chipeket dolgozott ki és dobott piacra, tavaly pedig már sajátmárkás kártyákkal is megjelent. Legújabb lapkakészlete, a GeForce 500-as sorozata több mint ezerszer akkora teljesítményre képes, mint a GeForce 256.
Sting

2011. január 12., szerda

HAL 9000

Arthur C. Clarke fantasztikus regényének, a 2001: Űrodüsszeiának mesterséges intelligenciával rendelkező számítógépét, a HAL 9000-et e napon – a filmadaptációban 1992. január 12-én – hozták működésbe az Illinois állambeli Urbanában. Ezt maga a gép árulja el, miközben a főszereplő, David Bowman deaktiválja.

Boldog Születésnapot HAL!

2011. január 9., vasárnap

Alphabetical BubbleSort

Néha jól jön egy rendező algoritmus, ami egy string tömb elemeit abc sorrendbe rakja. Itt egy lehetséges megvalósítás:
private string[] AlphabeticalBubbleSort(string[] values)
{
int i = 0;
int j = 0;
string temp;

for (i = values.GetUpperBound(0); i >= 0; i--)
{
for (j = 0; j <= i; j++)
{
if (values[j].ToString().ToLower().Trim() > values[j + 1].ToString().ToLower().Trim())
{
//Swap values
temp = values[j].ToString();
values[j] = values[j + 1];
values[j + 1] = temp;
}
}
}
return values;
}

C# tömbök mesteri szinten (2)

Egy picit még elidőzünk a múlt héten felvetett téma mellett. Nyilvánvaló, hogy a tömbök, főleg ha ciklusokon belül használjuk őket néha igen komoly teljesítményproblémát jelenthetnek, melyek egy része magától értetődő, más része rejtett. Az alábbiakban egy-két jellemző példát lehet majd látni erre.
Tételezzük fel, hogy egy táblázatot alkotó "jagged" tömb minden egyes sora kb. 4000 bájt méretű (figyelem, a méret a lényeg!). Ha ez a táblázat túl sok sorból áll, akkor minden esetben, amikor egy eltérő sor adataihoz hozzá szeretnénk férni, akkor a tábla/tömb és az állandó memória lapméretek miatt (mondjuk 4 KB) az operációs rendszernek úgynevezett memória-lapváltást kell végrehajtania. Másképp mondva hozzá kell férnie a tömbelem másik lapon lévő adataihoz. Vagyis a lenti példa szerint a belső ciklus miatt, minden tömbelemhez való hozzáférés újabb és újabb plusz memóriaműveletet (a már említett lapváltást) generál. Példa:

for (int column = 0; column < MAX_COLUMNS; column++)
{
for (int row = 0; row < MAX_ROWS; row++)
{
table[row][column] = BlankTableElement();
}
}
A fenti példa szerint tehát MAX_COLUMNS * MAX_ROWS darab memória-lapváltás történik, melyet mindenképp jó lenne kiküszöbölni, és erre az alábbi lehetőség áll a rendelkezésünkre.

for (int row = 0; row < MAX_ROWS; row++)
{
for (int column = 0; column < MAX_COLUMNS; column++)
{
table[row][column] = BlankTableElement();
}
}
Egyszerűen megcserélődött a két ciklus, vagyis ami eddig kívül volt az belül lesz és fordítva. Ebben az esetben is lesz memória-lapváltás, de már csak MAX_ROWS és nem MAX_COLUMNS * MAX_ROWS esetben. A teljesítménynövekedés feladattól és környezettől függően változhat, de bizonyos esetben, pl.: a második példa ciklusa akár 1000x előbb is befejeződhet, mint az első. Ha már a tömböknél tartunk... Az alábbi példa igaz, hogy a kód olvashatóságát rontja egy kissé, de teljesítmény szempontból, bizonyos helyzetekben rendkívül hatékony tud lenni (ez az úgynevezett „unrolling” művelet). Példa:

i = 0;
while (i < count)
{
a[i] = i;
i = i + 1;
}
Ez egy egyszerű ciklus volt, ami egy „a” numerikus tömböt feltöltött az „i” index értékével. A fenti megoldás helyett, amennyiben lehetséges (nyilván nem mindig), legyen inkább egy másik megoldás…

i = 0;
while (i < count - 1)
{
a[i] = i;
a[i + 1] = i + 1;
i = i + 2;
}
if (i == count)
{
a[count - 1] = count - 1;
}
A lényeg az, hogy a tömbön belül nem egyesével, hanem fura módon kettesével léptetjük az „i” indexet, és minden iterációnál kitöltjük az aktuális, valamint az aktuális i + 1 tömbelemet (vagy más feladat esetén végrehajtunk valami más tömbműveletet). Páratlan számú iteráció esetén az utolsó elem kitöltésére csak a ciklus után, külön kerülhet sor. A teljesítménynövekedés környezettől függően változhat, ezért az alkalmazása előtt méréseket kell végezni. Ahol a teljesítmény különösen kritikus és fontos, ott a .NET menedzselt kód által nyújtott plusz szolgáltatások kikerülhetők, azaz elérhető, hogy a tömbök adata ne a referencia típusokra jellemző menedzselt halmon (heap), hanem az annál jóval gyorsabban hozzáférhető, és a szemégyűjtő Garbage Collector által nem inzultált vermen (stack) tárolódjon. Az imént említett tömbhasználat felturbozásához a stackalloc nevű C# utasítást kell használni (fordításkor a /unsafe opció szükséges). Ez a módszer csak egy dimenziós, 0 bázisú, érték típusú elemeket tartalmazó tömböknél működhet. Amikor a stackalloc használatban van, akkor a memória blokkok eléréséhez úgynevezett unsafe pointer-t alkalmaz a rendszer. Ezek a memóriaterületek az adott hatókör (pl.: egy metódus return utasítása) után automatikusan felszabadulnak. Példa:

class StackallocSample
{
unsafe public static void ShowData(int* pArray)
{
for (int* ip = pArray; ip < (pArray+5); ip++)
{
Console.WriteLine("value {0} at address: {1}", *ip, (int)ip);
}
}

static void Main(string[] args)
{
unsafe
{
int* pArray= stackalloc int[5];
pArray[0] = 12;
pArray[1] = 34;
pArray[2] = 56;
pArray[3] = 78;
pArray[4] = 90;
ShowData(pArray);
}
}
}
Minden téma végén megjegyzem, hogy ezeket a megoldások és példák természetesen nem az én fejemben születtek. Ahhoz, hogy az ember hozzájusson a fenti okosságokhoz, egyszerűen csak olvasni kell, méghozzá sokat. "Mert olvasni nem gyíkság..."

Iteráció teljesítménynövelés

A múltkori témák adták az ötletet arra nézve, hogy néhány további, teljesítménynövelésre buzdító javaslatot tegyek, de most inkább csak az iterációkra (ciklusokra) vonatkoztatva. A korábbi példákhoz nagyon hasonló eset az, amikor az egymásba ágyazott ciklusok önmagában álló teljesítményét vizsgáljuk. Az utasítások működési jellemzőjéből adódóan, a belső ciklus minden egyes iterációjának alkalmával a ciklusváltozót inicializálni kell, az összehasonlító logikai kifejezést értékelni kell, valamint ciklusváltozót is növelni kell.

for (int column = 0; column < 100; column++)
{
for (int row = 0; row < 5; row++ )
{

}
}
A fenti példában kívül van a több, belül pedig a kevesebb iterációt végrehajtó ciklus. Ezesetben a ciklusváltozók menedzselése egészen pontosan 100 + (100 * 5) = 600 alkalommal hajtódik végre, ami kiélezett helyzetben tetemes időt vehet igénybe. Ilyenkor nincs más teendő, mint a külső, többször iteráló, illetve a belső, kevesebbszer iteráló ciklust egyszerűen felcserélni egymással.

for (int row = 0; row < 5; row++ )
{
for (int column = 0; column < 100; column++)
{

}
}
A javított változatban a ciklusváltozók menedzselése 5 + (5 * 100) = 505 alkalommal hajtódik végre, ami a 16%-kal kevesebb feladatot ró a működtető környezetre (ez utóbbi példa javára). Természetesen .NET esetén ez a példa picit erőltetett, mivel a JIT fordító számos optimalizálást már előre végre tud hajtani. A végső megvalósítás előtt ezesetben is érdemes méréseket végezni.
Szintén ciklusok esetén jellemző az, amikor mondjuk egy metóduson belül lévő ciklusban végre kell hajtani bizonyos műveleteket, aztán később ugyanilyen cikluson belül egy másik műveletet. Ezesetben érdemes megvizsgálni annak a lehetőségét, hogy a két azonos feltételekkel rendelkező ciklust nem lehetne-e összevonni (ezek az úgynevezett „jamming” vagy „fusion” műveletek).

for (int i = 0; i <= 100; i++)
{
employeeName[i] = "";
}
// Itt most különféle egyéb utasításokat, műveleteket kell elképzelni…
for (int i = 0; i <= 100; i++)
{
employeeEarnings[i] = 0;
}

A fenti megoldás helyett, amennyiben ez lehetséges, legyen inkább összevonás…

for (int i = 0; i <= 100; i++)
{
employeeName[i] = "";
employeeEarnings[i] = 0;
}
A ciklusokon belüli összetett műveletek mértékét a lehető legkisebbre kell venni. Ez azt jelenti, hogy minden idő és erőforrásigényes utasítás esetén meg kell vizsgálni, hogy nem lehet-e azt kiemelni a cikluson kívülre.

for (int i = 0; i < rateCount; i++)
{
netRate[i] = baseRate[i] * rates.CalculateDiscounts();
}
Ha a „rates” objektum „CalculateDiscounts()” metódusa mondjuk adatbázis, vagy más erőforrásigényes műveletet hajt végre, aminek a végeredménye az adott időben ugyanaz, akkor érdemes kiemelni azért, hogy csak egyszer hajtódjon végre (tudom, hogy ezek elemi dolgok, de hátha olyasvalaki is olvassa, aki csak most kezdi a programozói pályáját). Tudom-tudom... ezek primitív dolgok, de akkor is.

quantityDiscount = rates.CalculateDiscounts();
for (int i = 0; i < rateCount; i++)
{
netRate[i] = baseRate[i] * quantityDiscount;
}
Amennyiben egy cikluson belül már nincs értelme a további iterációnak, akkor gondoskodni kell az iteráció azonnali, vagy legalábbis a lehető leghamarabb történő befejezéséről. Minden további felesleges műveletet teljesítmény és erőforrás gazdálkodási okokból kerülni kell.
Put About