LINUXSOFT.cz Přeskoč levou lištu
Uživatel: Heslo:  
   CZUKPL

> Perl (76) - Síťová hra v kostky

Perl Dnes využijeme znalosti nabyté v předchozích dílech a napíšeme si jednoduchý server pro hru v kostky, který bude organizovat hru libovolnému množství hráčů.

24.2.2009 06:00 | Jiří Václavík | Články autora | přečteno 10017×

Cílem dnešního dílu bude vytvořit základ pro v podstatě libovolnou síťovou hru. V článku půjde konkrétně o hru v kostky. To proto, že hra v kostky je velice jednoduchá na programování. Budou tak více vidět obecnější myšlenky a síťová komunikace.

Plán

Nejprve si tedy uvědomme, co vlastně budeme psát.

Hru v kostky zná snad každý - spočívá v opakovaném házení kostkou dvěma hráči, přičemž ten, kdo hodí v ntém hodu více ok, získává bod. V případě shodnosti počtu ok nezíská bod nikdo. Kdo získá 5 bodů, vítězí.

Úkolem tedy bude napsat nějaký kostkový server. Ten bude čekat na klienty. Jakmile se přihlásí klient, zaregistruje ho. Tento klient bude čekat na to, až se přihlásí nějaký další klient, s kterým by mohl začít hru. Jakmile budou k dispozici dva nehrající klienti, založíme jim automaticky hru. A stále budeme čekat na další.

Jinými slovy, v tuto chvíli budeme dělat několik věcí zároveň:

  • pro každou již přihlášenou dvojici budeme organizovat hru
  • pro další klienty budeme hledat vhodné protějšky na přihlášení do hry

Jde o krátký program, takže to nyní není třeba do detailů rozebírat. Vše bude jasné v průběhu.

Server

Hned teď si ukažme celý skript server.pl. Budeme využívat modulu Kostky::Server, který budeme muset ještě dodělat.

#!/usr/bin/perl -w
use strict;
use warnings;
use Kostky::Server;

while (1){
    my $vitez;
    my $kostky_server = new Kostky::Server;
    $kostky_server->spust;
}

Nyní se pojďme podívat, jak bude vypadat základ modulu Kostky::Server (tj. soubor Kostky/Server.pm).

package Kostky::Server;
use strict;
use warnings;
use IO::Socket;

sub new {
    my($self) = @_;
    my $f = {};
    bless $f;

    my $hrac = IO::Socket::INET->new(
        Proto     => "tcp",
        LocalPort => 9005,
        LocalAddr => "localhost",
        Listen    => 2,
        Reuse     => 1
    ) or die "Nelze vytvorit socket! $!";

    $f->{"hrac"} = $hrac;
    $f->{"skore1"} = 0;
    $f->{"skore2"} = 0;

    return $f;
}

sub spust {
    ...
}

Zatím šlo pouze o rutinní záležitosti a není zde nic, co bychom nečekali. Tj. v kostruktoru jsme vytvořili spojení (jsme serveru), počet klientů nastavili na 2 (každou hru budou hrát pouze 2 hráči) a skóre jsme nastavili na 0:0.

Nyní bude naším úkolem implementovat metodu spust, která bude dělat veškerou práci serveru.

Máme za úkol řídit hru libovolnému množství hráčů, tj. použijeme fork na odštěpení jednotlivých instancí hry. Tentokráte půjde dokonce o jednu instanci pro dva hráče. Schéma metody bude tedy vypadat takto.

sub spust {
    while(1){

        #připojení dvou hráčů

        next unless $pid = fork;

        #obsloužíme odštěpený proces

        exit;
    }
}

Hledání dvojic hráčů

Připojení dvou hráčů znamená naplnění proměnných $self->{"vzdaleny1"}, $self->{"vzdaleny2"}, $self->{"nick1"}, $self->{"nick2"} hodnotami. To znamená, že musíme získat hodnoty pro spojení na oba hráče a jejich jména. Na ty se zeptáme klientů, jakmile se připojí - a to tak, že jim odešleme zprávu s obsahem "1\n". Už bude na klientovi, aby tomuto protokolu rozumněl (tj. o to se budeme starat později).

Oba hráče připojíme pomocí cyklu o dvou iteracích. Samozřejmě to lze rozepsat do jednotlivých iterací.

        for(my $i=1; $i<=2; $i++){
            $vzdaleny = $self->{"hrac"}->accept();
            $vzdaleny->autoflush(1);
            print $vzdaleny "1\n";
            chomp($nick = <$vzdaleny>);
            do_logu("-", "$nick pripojen");
            $self->{"vzdaleny$i"} = $vzdaleny;
            $self->{"nick$i"} = $nick;
        }

Všimněme si tohoto řádku.

print "$nick pripojen\n";

Pokud použijeme print směrovaný jinam než do socketu, bude to něco jako logování. Nepoužíváme zde log soubor, ale směřujeme výstup na standardní výstup. Logovat budeme po každé akci.

Vhodnější by bylo logování nějak sjednotit. Odteď budeme v lozích zaznamenávat také čas a PID (neboť mícháme výstup všech her do jednoho logu). Pro tento účel si napíšeme jednoduchou funkci do_logu, která pošle jeden log záznam ve formátu 'PID čas zpráva' na standardní výstup.

sub do_logu {
    my($pid, $zprava)=@_;
    my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time);
    print "$pid $hour:$min:$sec $zprava\n";
}

Řízení hry

Nyní již pojďme vyřídit odštěpený proces. Zahájíme hru. Při té příležitosti zapíšeme do logu následující zprávu.

        do_logu($pid, "V teto hre hraji ".$self->{"nick1"}." a ".$self->{"nick2"});

Před tím, než napíšeme hlavní smyčku hry, nastavme kritéria pro konec hry. Funkce konec nám vyhodnotí, zda někdo vyhrál a případně kdo. Při naší hře v kostky je kritérium jediné - stačí dosáhnout 5 bodů.

sub konec {
    my($self) = @_;
    my $vitez;

    return 1 if $self->{"skore1"}==$SKORE_VITEZE;
    return 2 if $self->{"skore2"}==$SKORE_VITEZE;
    return -1; #není vítěz
}

A teď konečně přistupme k jádru serveru - napíšeme onu hlavní smyčku. Bude vypadat nějak takto.

        while(($vitez = $self->konec) == -1) {
            $cislo_tahu++;

            #nalosujeme, kdo bude v tomto tahu začínat
            #necháme hráče "hodit" kostkou
            #vyhodnotíme hody, a pošleme hráčům výsledek
        }

Začneme tedy losem, kdo bude začínat aktuální tah. My to uděláme tak, že v polovině případů prohodíme hráče a v polovině ne. Toto řešení má své výhody i nevýhody, nicméně nám plně postačuje.

            if((rand(2)>1 ? 1 : 0)==0){
                ($self->{"vzdaleny1"}, $self->{"vzdaleny2"}, $self->{"nick1"},
$self->{"nick2"}) = ($self->{"vzdaleny2"}, $self->{"vzdaleny1"}, $self->{"nick2"},
$self->{"nick1"});
            }

Pro jednoduchost nyní zavedeme proměnné $klient1 a $klient2 jako aliasy pro spojení na hráče.

            $klient1 = $self->{"vzdaleny1"};
            $klient2 = $self->{"vzdaleny2"};

Dále musíme hráčům oznámit, kdo bude začínat a kdo bude hrát jako druhý. Pošleme tedy zprávu "1\n" hráči, který bude začínat a zprávu "2\n" hráči, který musí počkat na jeho výsledek.

            print $klient1 "1\n";#hráč1 začíná
            print $klient2 "2\n";#hráč1 čeká

Dále čekáme na hráče, který má začínat, až hodí. Jakmile se tak stane, podíváme se, jaké číslo je kostce pomocí funkce rand (tj. v okamžiku, kdy hodí, stopneme čas a podle něj určíme počet ok).

            do_logu($pid, "Cekame, az hodi ".$self->{"nick1"});
            my $hod = <$klient1>;
            my $vysledek_hodu1 = int(rand(6))+1;

Výsledek oznámíme oběma hráčům.

            print $klient1 "$vysledek_hodu1\n";
            print $klient2 "$vysledek_hodu1\n";
            do_logu($pid, $self->{"nick1"}." hodil $vysledek_hodu1");

Následně čekáme na hod od druhého hráče.

            do_logu($pid, "Cekame, az hodi ".$self->{"nick2"});
            $hod = <$klient2>;
            my $vysledek_hodu2 = int(rand(6))+1;

Výsledek taktéž oznámíme.

            print $klient2 "$vysledek_hodu2\n";
            print $klient1 "$vysledek_hodu2\n";
            do_logu($pid, $self->{"nick2"}." hodil $vysledek_hodu2");

Na závěr zaktualizujeme skóre (přidáme tomu hráči, který hodil na kostce více) a výsledky pošleme.

            #aktualizujeme skore
            $self->{"skore1"}++ if $vysledek_hodu1>$vysledek_hodu2;
            $self->{"skore2"}++ if $vysledek_hodu1<$vysledek_hodu2;
    
            print $klient1 $self->{"nick1"}." vs. ".$self->{"nick2"}." ".
$self->{"skore1"}."-".$self->{"skore2"}."\n";
            print $klient2 $self->{"nick1"}." vs. ".$self->{"nick2"}." ".
$self->{"skore1"}."-".$self->{"skore2"}."\n";
            do_logu($pid, $self->{"nick1"}." vs. ".$self->{"nick2"}." ".
$self->{"skore1"}."-".$self->{"skore2"});

Tím je hlavní cyklus hotov. Za něj se program dostane pouze tehdy, je-li znám vítěz. Proměnnou $vitez již máme naplněnou - z hlavičky cyklu. Stačí tedy odeslat celkové výsledky a jsme hotovi.

        $porazeny = $vitez==1?2:1;
        print {$self->{"vzdaleny$vitez"}} "-1\n";
        print {$self->{"vzdaleny$porazeny"}} "-2\n";

Tím jsme dokončili herní server.

Klienti

Pojďme se podívat na druhou část a tou bude napsání klienta, který bude umět s naším serverem komunikovat.

Pro jednoduchost nebudeme psát žádný speciální modul, ač by to šlo (a při náročnější hře by to bylo vhodné), ale spokojíme se se skriptem, který bude vše řešit sám.

#!/usr/bin/perl -w
use strict;
use warnings;
use IO::Socket;

Nejprve od uživatele zjistíme, kde server běží.

print "Zadej IP adresu serveru: ";
chomp($ip_serveru = <STDIN>);

Server po nás taktéž bude chtít zadat jméno, takže se na něj hned také zeptáme.

print "Zadej nick: ";
chomp($nick = <STDIN>);

Teď se již k serveru můžeme připojit a odešleme mu naše jméno. Pokud obdržíme od serveru odpověď, je to dobrá zpráva, protože se nám připojit se podařilo.

my $vzdaleny = IO::Socket::INET->new(
    Proto     => "tcp",
    PeerPort  => 9005,
    PeerAddr  => $ip_serveru
) or die "Nelze vytvorit socket! $!";

print $vzdaleny "$nick\n";
print "Jsme pripojeni!\n" if <$vzdaleny>;

Nyní to již nebude těžké - budeme pouze reagovat na to, na co se server ptá. Půjde jen o zesynchronizování se se serverem. Program bude samozřejmě opět ve formě smyčky. Na začátku každého tahu nám server posíla informaci o tom, zda začínáme nebo ne. Proto od něj očekáváme zprávu.

while(1){
    my $na_tahu = <$vzdaleny>;
    chomp $na_tahu;

    if($na_tahu == 1){
        #hrajeme jako první
    }elsif($na_tahu == 2){
        #hrajeme až po soupeři
    }
    ...
}

Místo tří teček ještě vepíšeme následující kód, neboť právě tímto způsobem server oznamuje vítězství či porážku.

    elsif($na_tahu == -1){
        print "JSI VITEZ\n";
        last;
    }elsif($na_tahu == -2){
        print "JSI PORAZENY\n";
        last;
    }

Na závěr dokončíme první dvě podmínky. Zde jen recipročně reagujeme na server.

    if($na_tahu == 1){
        print "Mas v ruce kostku. Stiskni ENTER pro hod!";
        $hod = <STDIN>;
        print $vzdaleny "1\n";
        chomp($vysledek_hodu = <$vzdaleny>);
        print "Hodil jsi $vysledek_hodu. Cekame na soupere.\n";
        chomp($vysledek_soupere = <$vzdaleny>);
        print "Souper hodil $vysledek_soupere.\n";
        $skore = <$vzdaleny>;
        print $skore;
    }elsif($na_tahu == 2){
        print "Cekame na hod soupere.\n";
        chomp($vysledek_soupere = <$vzdaleny>);
        print "Souper hodil $vysledek_soupere.\n";
        print "Mas v ruce kostku. Stiskni ENTER pro hod!";
        $hod = <STDIN>;
        print $vzdaleny $hod;
        chomp($vysledek_hodu = <$vzdaleny>);
        print "Hodil jsi $vysledek_hodu.\n";
        $skore = <$vzdaleny>;
        print $skore;
    }

Tím jsme hotovi s klientem.

Závěr

Nyní máme server i klienty hotové. Myšlenkově nešlo o nic náročného, ovšem při těchto aplikacích zabere často hodně času ladění.

Všechny tři soubory si můžete stáhnout a vyzkoušet: server.pl, klient.pl, Kostky/Server.pm.

Můžeme nyní vyzkoušet, jak se dá náš server použít.

Server *** Klient1 *** Klient2 *** Klient3

Klient4 *** Klient5 *** Klient6

Na základě této hry bychom nyní mohli analogicky napsat cokoliv jiného - například síťové šachy, piškvorky a jiné. Z hlediska programování nebudou o moc složitější. Půjde pouze o více psaní.

Verze pro tisk

pridej.cz

 

DISKUZE

Nejsou žádné diskuzní příspěvky u dané položky.



Příspívat do diskuze mohou pouze registrovaní uživatelé.
> Vyhledávání software
> Vyhledávání článků

15.5.2017 23:50 /František Kučera

Máš rád svobodný software a hardware nebo se o nich chceš něco dozvědět? Zajímá tě DIY, CNC, SDR nebo morseovka? Přijď na sraz spolku OpenAlt, který se bude konat ve čtvrtek 18. května od 18:00 v Radegastovně Perón (Stroupežnického 20, Praha 5).


Přidat komentář

12.5.2017 16:42 /Honza Javorek
PyCon CZ, česká konference o programovacím jazyce Python, se po dvou úspěšných ročnících v Brně bude letos konat v Praze, a to 8. až 10. června. Na konferenci letos zavítá např. i Armin Ronacher, známý především jako autor frameworku Flask, šablon Jinja2/Twig, a dalších projektů. Těšit se můžete na přednášky o datové analytice, tvorbě webu, testování, tvorbě API, učení a mentorování programování, přednášky o rozvoji komunity, o použití Pythonu ve vědě nebo k ovládání nejrůznějších zařízení (MicroPython). Na vlastní prsty si můžete na workshopech vyzkoušet postavit Pythonem ovládaného robota, naučit se učit šestileté děti programovat, efektivně testovat nebo si v Pythonu pohrát s kartografickým materiálem. Kupujte lístky, dokud jsou.
Přidat komentář

2.5.2017 9:20 /Eva Rázgová
Putovní konference československé Drupal komunity "DrupalCamp Československo" se tentokrát koná 27. 5.2017 na VUT FIT v Brně. Můžete načerpat a vyměnit si zkušenosti z oblasti Drupalu 7 a 8, UX, SEO, managementu týmového vývoje, využití Dockeru pro Drupal a dalších. Vítáni jsou nováčci i experti. Akci pořádají Slovenská Drupal Asociácia a česká Asociace pro Drupal. Registrace na webu .
Přidat komentář

1.5.2017 20:31 /Pavel `Goldenfish' Kysilka
PR: 25.5.2017 proběhne v Praze konference na téma Firemní informační systémy. Hlavními tématy jsou: Informační systémy s vlastní inteligencí, efektivní práce s dokumenty, mobilní přístup k datům nebo využívání cloudu.
Přidat komentář

15.4.2017 15:20 /František Kučera
Máš rád svobodný software a hardware nebo se o nich chceš něco dozvědět? Zajímá tě IoT a radiokomunikace? Přijď na sraz spolku OpenAlt, který se bude konat ve středu 19. dubna od 18:30 v Šenkovně (Sokolská 60, Praha 2).
Přidat komentář

5.3.2017 19:12 /Redakce Linuxsoft.cz
PR: 23. března proběhne v Praze konferenci na téma Cloud computing v praxi. Hlavními tématy jsou: Nejžhavější trendy v oblasti cloudu a cloudových řešení, Moderní cloudové služby, Infrastruktura současných cloudů, Efektivní využití cloudu, Nástrahy cloudových řešení a jak se jim vyhnout.
Přidat komentář

27.2.2017 22:12 /František Kučera
Pozvánka na 137. sraz OpenAlt – Praha: Tentokrát jsme si pro vás připravili neobvyklou akci. Ve středu 1.3. v 17:30 nás přivítá sdružení CZ.NIC ve svých prostorách v Milešovské ulici číslo 5 na Praze 3, kde si pro nás připravili krátkou prezentaci jejich činnosti. Následně navštívíme jejich datacentrum pod Žižkovskou věží. Provedou nás prostory, které jsou běžnému smrtelníkovi nedostupné!
Po ukončení prohlídky se všchni odebereme do hostince U vodoucha, Jagelonská 21, Praha 3 pochutnat si na některém z vybraných piv či dát si něco na zub. Rezervaci máme od 19:30, heslo je OpenAlt.
Ale pozor! Do prostor datového centra máme omezený přístup, dostane se tam pouze 10 lidí! Takže kdo přijde dříve, ten má přednost, a občanky s sebou! Kdo nebude chtít na prohlídku datového centra, může se pomalu přesunout do hostince U vodoucha a u nepřeberné nabídky piv počkat na ostatní.
Přidat komentář

18.1.2017 0:49 /František Kučera
Členové a příznivci spolku OpenAlt se pravidelně schází v Praze a Brně. Fotky z pražských srazů za uplynulý rok si můžete prohlédnout na stránkách spolku. Příští sraz se koná už 19. ledna – tentokrát je tématem ergonomie ovládání počítače – tzn. klávesnice, myši a další zařízení. Také budete mít příležitost si prohlédnout pražský hackerspace Brmlab.
Přidat komentář

   Více ...   Přidat zprávičku

> Poslední diskuze

15.6.2017 9:34 / Ondřej Havlas
php,

10.6.2017 10:39 / Temple
sell home for cash

11.5.2017 23:32 / lelo
Re: Problém se správcem balíčků

11.5.2017 5:45 / davd mašek
Re: Problém se správcem balíčků

10.5.2017 22:54 / lelo
Re: Problém se správcem balíčků

Více ...

ISSN 1801-3805 | Provozovatel: Pavel Kysilka, IČ: 72868490 (2003-2017) | mail at linuxsoft dot cz | Design: www.megadesign.cz | Textová verze