Tunnus:

Salasana:

Uusi käyttäjä

Haku

Pikalinkit

Kesähaaste 2010

Paranna Morpion-pelin kansainvälisiä ennätyksiä!

Ohjeet | Nettipeli | Tuloslista

Putkaposti

Suunnittele tiedosto, josta tulee suuri ZIP-paketti!

Vastauksia: 32
Paras: 1158

Tehtävään...

Keskustelu

Millä tavalla aiemmissa keskusteluissa esitetyn formin sijoittaminen tuohon linkin paikalle tuottaa ongelmia? (Nettisivut ja -ohjelmointi) lisää...


Koodivinkit: Muut: Sekalaiset: Ristinollan verkkopelitoteutus Infernolle Limbolla

Kirjoittaja: jalski (29.11.2009)

Kahden pelattava ristinolla client-server peli Infernolle Limbo-ohjelmointikielellä. Pelin verkkoliikenteen oletusporttina toimii portti 2000.

Listaukset

implement RistiNolla;

include "sys.m";
   sys: Sys;
   Dir: import sys;
   Connection : import Sys;
 
include "draw.m";
   draw: Draw;
   Screen, Display, Image, Context, Point, Rect: import draw;

include "tk.m";
   tk: Tk;
   Toplevel: import tk;

include "tkclient.m";
   tkclient: Tkclient;
   Hide: import tkclient;

include "dialog.m";
   dialog : Dialog;


RistiNolla : module
{
   init: fn(ctxt: ref Draw->Context, argv: list of string);
};

PORT : con "2000";
WINBUT : con Hide;
BSZ : con 20;
BSZI : con BSZ + 2;
EMPTY, PLAYER1, PLAYER2 : con iota;

# board[frame][button]
board := array[BSZI] of { * => array[BSZI] of {* => EMPTY} };

ctxt: ref Draw->Context;
mainwin : ref Tk->Toplevel;

workerpid : int;

cmd : chan of string;
gamecmd : chan of string;
localcmd : chan of string;
remotecmd : chan of string;



init(xctxt: ref Draw->Context, nil: list of string)
{
   sys  = load Sys Sys->PATH;
 
   if (xctxt == nil) {
      sys->fprint(sys->fildes(2), "testi: no window context\n");
      raise "fail:bad context";
   }

   ctxt = xctxt;

   draw = load Draw Draw->PATH;
   tk   = load Tk   Tk->PATH;
   tkclient = load Tkclient Tkclient->PATH;
   dialog = load Dialog Dialog->PATH;

   sys->pctl(Sys->NEWPGRP, nil);

   tkclient->init();
   dialog->init();

   wmctl : chan of string;  
   (mainwin, wmctl) = tkclient->toplevel(ctxt, nil, "RistiNolla", WINBUT);
   if(mainwin == nil)
   {
      sys->fprint(sys->fildes(2), "RistiNolla: creation of toplevel window failed\n");
      raise "fail:creation of toplevel window failed";
   }

   gamecmd = chan of string;

   cmd = chan of string;
   tk->namechan(mainwin, cmd, "cmd");

   localcmd = chan of string;
   tk->namechan(mainwin, localcmd, "pcmd");

   remotecmd = chan of string;

   display_board();

   center(mainwin);
   tkclient->onscreen(mainwin, "exact");
   tkclient->startinput(mainwin, "kbd"::"ptr"::nil);


   for(;;) alt {
      s := <- mainwin.ctxt.kbd =>
         tk->keyboard(mainwin, s);

      s := <- mainwin.ctxt.ptr =>
         tk->pointer(mainwin, *s);

      s := <- mainwin.ctxt.ctl or
      s = <- mainwin.wreq or
      s = <- wmctl =>
         case s {
            "exit" =>
               fd := sys->open("#p/" + string workerpid +"/ctl", sys->OWRITE);
               if(fd != nil)
                  sys->fprint(fd, "kill");

               tkclient->wmctl(mainwin, "exit");

            * =>
               tkclient->wmctl(mainwin, s);
         }

      menucmd := <- cmd =>
         case menucmd {
            "host" =>
            reset_board();
            (n, conn) := sys->announce("tcp!*!" + PORT);
            if (n < 0)
            {
               tk->cmd(mainwin,".ft.ls configure -text {Pelin isännöinti epäonnistui}");
               tk->cmd(mainwin, "update");              
            }
            else
            {
               spawn listenthread(conn);

               tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state disabled");
               tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state disabled");
               tk->cmd(mainwin,".ft.ls configure -text {Odotetaan toista pelaajaa}");
               tk->cmd(mainwin, "update");              
            }

            "join" =>
               reset_board();
               address := "tcp!" + dialog->getstring(ctxt,mainwin.image, "Anna isännän osoite") + "!" + PORT;
               (ok, conn) := sys->dial(address, "");
               if (ok < 0)
               {
                  tk->cmd(mainwin,".ft.ls configure -text {Yhteyden muodostus epäonnistui}");
                  tk->cmd(mainwin, "update");              
               }
               else
               {
                  tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state disabled");
                  tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state disabled");
                  tk->cmd(mainwin,".ft.ls configure -text {Vastustajan vuoro}");
                  tk->cmd(mainwin, "update");

                  spawn workerthread(conn);
                  spawn runclient(conn);

               }
 

         }

        
   }

}



runserver(conn : Connection)
{

   absorb(localcmd);
   localch := localcmd;
   dummych := chan of string;


   for(;;) alt {

      game := <- gamecmd =>
         case game {
            "close" =>
                tk->cmd(mainwin,".ft.ls configure -text {Toinen pelaaja sulki yhteyden}");
                tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state normal");
                tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state normal");
                tk->cmd(mainwin, "update");
                exit;
         }
 
      # täällä käsitellään tiedot pelilaudan painalluksista
      local := <-localch =>
         localch = dummych;
         (n, tokens) := sys->tokenize(local, " ");
         if(n >= 3)
            case hd tokens {
               "b" =>
                  row := int hd tl tokens;
                  column := int hd tl tl tokens;

                  if(board[row][column] == EMPTY)
                  {
                     message := local + "\r\n";
                     wdfd := sys->open(conn.dir + "/data", Sys->OWRITE);
                     sys->write(wdfd, array of byte message, len array of byte message);

                     board[row][column] = PLAYER1;

                     tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -text {X}", row, column));

                    if(testrows(row, column, 5, PLAYER1) == 1)
                     {
                        tk->cmd(mainwin,".ft.ls configure -text {Voitit!}");
                        tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state normal");
                        tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state normal");
                        tk->cmd(mainwin, "update");

                        fd := sys->open("#p/" + string workerpid +"/ctl", sys->OWRITE);
                        if(fd != nil)
                           sys->fprint(fd, "kill");

                        exit;
                     }

                     tk->cmd(mainwin,".ft.ls configure -text {Vastustajan vuoro}");
                     tk->cmd(mainwin, "update");
          
                  }
                  else
                     localch = localcmd;

                  * =>
                     localch = localcmd;      
         }
         else
            localch = localcmd;  


      remote := <-remotecmd =>
         (n, tokens) := sys->tokenize(remote, " ");
         if(n >= 3)
            case hd tokens {
               "b" =>
                  row := int hd tl tokens;
                  column := int hd tl tl tokens;

                  board[row][column] = PLAYER2;

                  tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -text {0}", row, column));

                  if(testrows(row, column, 5, PLAYER2) == 1)
                  {
                     tk->cmd(mainwin,".ft.ls configure -text {Vastustaja voitti!}");
                     tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state normal");
                     tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state normal");
                     tk->cmd(mainwin, "update");

                     fd := sys->open("#p/" + string workerpid +"/ctl", sys->OWRITE);
                     if(fd != nil)
                        sys->fprint(fd, "kill");
              
                     exit;
                  }


                  tk->cmd(mainwin,".ft.ls configure -text {Oma vuoro}");
                  tk->cmd(mainwin, "update");

                  absorb(localcmd);
                  localch = localcmd;
                
            }
  

   }

  

}




runclient(conn : Connection)
{

   dummych := chan of string;
   localch := dummych;

   for(;;) alt {

      game := <- gamecmd =>
         case game {
            "close" =>
                tk->cmd(mainwin,".ft.ls configure -text {Toinen pelaaja sulki yhteyden}");
                tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state normal");
                tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state normal");
                tk->cmd(mainwin, "update");
                exit;
         }
 
      # täällä käsitellään tiedot pelilaudan painalluksista
      local := <-localch =>
         localch = dummych;
         (n, tokens) := sys->tokenize(local, " ");
         if(n >= 3)
            case hd tokens {
               "b" =>
                  row := int hd tl tokens;
                  column := int hd tl tl tokens;

                  if(board[row][column] == EMPTY)
                  {
                     message := local + "\r\n";
                     sys->write(conn.dfd, array of byte message, len array of byte message);

                     board[row][column] = PLAYER2;

                     tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -text {0}", row, column));

                     if(testrows(row, column, 5, PLAYER2) == 1)
                     {
                        tk->cmd(mainwin,".ft.ls configure -text {Voitit!}");
                        tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state normal");
                        tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state normal");
                        tk->cmd(mainwin, "update");

                        fd := sys->open("#p/" + string workerpid +"/ctl", sys->OWRITE);
                        if(fd != nil)
                           sys->fprint(fd, "kill");
  
                        exit;
                     }

                     tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -text {0}", row, column));

                     tk->cmd(mainwin,".ft.ls configure -text {Vastustajan vuoro}");
                     tk->cmd(mainwin, "update");
          
                  }
                  else
                     localch = localcmd;

                  * =>
                     localch = localcmd;      
         }
         else
            localch = localcmd;  


      remote := <-remotecmd =>
         (n, tokens) := sys->tokenize(remote, " ");
         if(n >= 3)
            case hd tokens {
               "b" =>
               row := int hd tl tokens;
               column := int hd tl tl tokens;

               board[row][column] = PLAYER1;

               tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -text {X}", row, column));

               if(testrows(row, column, 5, PLAYER1) == 1)
               {
                  tk->cmd(mainwin,".ft.ls configure -text {Vastustaja voitti!}");
                  tk->cmd(mainwin,".f.menu.gm entryconfigure 0 -state normal");
                  tk->cmd(mainwin,".f.menu.gm entryconfigure 1 -state normal");
                  tk->cmd(mainwin, "update");

                  fd := sys->open("#p/" + string workerpid +"/ctl", sys->OWRITE);
                  if(fd != nil)
                     sys->fprint(fd, "kill");

                  exit;
               }

               tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -text {X}", row, column));

               tk->cmd(mainwin,".ft.ls configure -text {Oma vuoro}");
               tk->cmd(mainwin, "update");

               absorb(localcmd);
               localch = localcmd;
                
         }
  

   }

  

}





center(t: ref Tk->Toplevel)
{
   org: Point;
   ir := tk->rect(t, ".", Tk->Border|Tk->Required);
   org.x = t.screenr.dx() / 2 - ir.dx() / 2;
   org.y = t.screenr.dy() / 2 - ir.dy() / 2;

   if (org.y < 0)
   {
      org.y = 0;
   }

   tk->cmd(t, ". configure -x " + string org.x + " -y " + string org.y);
}



display_board()
{
   i, j: int;
   pack: string;

   tk->cmd(mainwin, "frame .f");
   tk->cmd(mainwin, "pack .f -fill x");
   tk->cmd(mainwin, "menubutton .f.menu -text Peli -menu .f.menu.gm");
   tk->cmd(mainwin, "menu .f.menu.gm");
   tk->cmd(mainwin, ".f.menu.gm add command -label {isännöi} -command {send cmd host}");
   tk->cmd(mainwin, ".f.menu.gm add command -label {liity} -command {send cmd join}");
   tk->cmd(mainwin, "pack .f.menu -side left");

   for (i = 1; i <= BSZ; i++)
   {
      tk->cmd(mainwin,  sys->sprint("frame .f%d", i));
      pack = "";
      
      for (j = 1; j <= BSZ; j++)
      {
         pack += sys->sprint(" .f%d.b%d", i, j);
         tk->cmd(mainwin, sys->sprint("button .f%d.b%d -text { } -width 14 -command {send pcmd b %d %d}", i, j, i, j));
      }

      tk->cmd(mainwin, sys->sprint("pack %s -side left", pack));
      tk->cmd(mainwin, sys->sprint("pack .f%d -side top -fill x", i));
  
   }
  
   tk->cmd(mainwin, "frame .ft");
   tk->cmd(mainwin, "label .ft.li -text {Tila: }");
   tk->cmd(mainwin, "label .ft.ls -text {Ei yhteydessä}");
   tk->cmd(mainwin, "pack .ft.li .ft.ls -side left -fill x");
   tk->cmd(mainwin, "pack .ft -side bottom -fill x");
   tk->cmd(mainwin, "update");

}





listenthread(conn : Connection)
{
          
   (ok, c) := sys->listen(conn);
   if (ok < 0)
   {
      sys->fprint(sys->fildes(2), "Server: listen failed\n");
      raise "fail:listen failed";
   }


   tk->cmd(mainwin,".ft.ls configure -text {Oma vuoro}");
   tk->cmd(mainwin, "update");

   spawn runserver(c);
   spawn workerthread(c);

}



workerthread(conn : Connection)
{
   workerpid = sys->pctl(0, nil);

   buf := array [1] of byte;
   rdfd := sys->open(conn.dir + "/data", Sys->OREAD);      
   output := "";

   while( (n := sys->read(rdfd, buf, len buf ) ) > 0 )
   {    
      output[len output] = int buf[0];

      if(len output >= 2)
      {
         if(output[len output - 2:] == "\r\n")
         {
            remotecmd <- = output[:len output - 2];
            output = "";
         }

      }

   }

   gamecmd <- = "close";


}





absorb(ch : chan of string)
{
   for(;;)
   {
      alt
      {
         <- ch =>
            ;
         * =>
            return;
      }
   }
}



length(row, column, drow, dcolumn, item: int): int
{
   l := 0;

   while(board[row][column] == item)
   {
      row += drow;
      column += dcolumn;
      
      l++;
   }

   return l;

}



testrows(row, column, items, item: int) : int
{
   vaaka := (length(row, column, 0, -1, item) + length(row, column, 0, 1, item) - 1);
   if(vaaka >= items)
   {
      mark_board(row, column, 0, -1, item);
      mark_board(row, column, 0, 1, item);
      return 1;
   }

   pysty := (length(row, column, -1, 0, item) + length(row, column, 1, 0, item) - 1);
   if(pysty >= items)
   {
      mark_board(row, column, -1, 0, item);
      mark_board(row, column, 1, 0, item);
      return 1;
   }

   vino1 := (length(row, column, -1, -1, item) + length(row, column, 1, 1, item) - 1);
   if(vino1 >= items)
   {
      mark_board(row, column, -1, -1, item);
      mark_board(row, column, 1, 1, item);
      return 1;
   }

   vino2 := (length(row, column, -1, 1, item) + length(row, column, 1, -1, item) - 1);
   if(vino2 >= items)
   {
      mark_board(row, column, -1, 1, item);
      mark_board(row, column, 1, -1, item);
      return 1;
   }

   return 0;
  
}




mark_board(row, column, drow, dcolumn, item : int)
{
   while(board[row][column] == item)
   {
      tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -bg olive -activebackground  olive", row, column));
      
      row += drow;
      column += dcolumn;
   }

}


reset_board()
{
   # nollaa board
   for (i := 0; i < BSZI; i++)
      for (j := 0; j < BSZI; j++)
         board[i][j] = EMPTY;
  
   # tyhjennä board näytöllä
   for (i = 1; i <= BSZ; i++)
      for (j = 1; j <= BSZ; j++)
         tk->cmd(mainwin, sys->sprint(".f%d.b%d configure -text { } -bg #dddddd -activebackground #eeeeee", i,  j));

   tk->cmd(mainwin, "update");

}


trilog [16.12.2009 13:36:20]LainaaMuokkaa
Mitä hyötyä tuollaisesta koodivinkistä on, jossa on ~600 riviä ja viisi kommenttia? Nekin vähäiset kommentit ovat ihan turhanpäiväisiä.
jalski [16.12.2009 21:29:13]LainaaMuokkaa
trilog kirjoitti:
Mitä hyötyä tuollaisesta koodivinkistä on, jossa on ~600 riviä ja viisi kommenttia? Nekin vähäiset kommentit ovat ihan turhanpäiväisiä.

Se on koodivinkki eikä mikään helv*tin tutoriaali.

Tuosta n. 600 rivistä tarvitsee oivaltaa vain miten kanavat toimivat ja lopun ymmärtäminen ei ole enää kovinkaan vaikeaa.

Ohjelmahan koostuu vain muutamasta säikeestä ja homma pysyy kasassa kanavien avulla.
tsuriga [03.01.2010 14:55:10]LainaaMuokkaa
Siinä tapauksessa olis ollu varmaan hyvä kirjotella jotain niistä kanavista. Koodivinkkien ohjeissa kyllä mainitaan kommentointivaatimukset.
jalski [03.01.2010 19:38:50]LainaaMuokkaa
tsuriga kirjoitti:
Siinä tapauksessa olis ollu varmaan hyvä kirjotella jotain niistä kanavista. Koodivinkkien ohjeissa kyllä mainitaan kommentointivaatimukset.

Innostuin kirjoittamaan tuon Infernon ristinolla toteutuksen Limbolla Macron pähkäillessä Python toteutuksen kanssa. Postitin sen koodivinkkeihin, koska mielestäni siitä tuli ihan hyvä, lyhyt ja yksinkertainen toteutus.

Laitanpa haasteen: kirjoita omaa esimerkkiäni yksinkertaisempi graafinen kahden pelattava verkkopelitoteutus.

Ohjelman ulkoasuksi riittänee omaani vastaava: http://www.tip9ug.jp/who/jalih/rn3.jpg

Vastailen muuten ihan mielelläni, jos jollain on esimerkistäni jotain asiallista kommentoitavaa tai kysymyksiä.
juketsu [22.01.2010 17:00:28]LainaaMuokkaa
Tähän olisi kiva saada lisä, että jokaisesta voitosta menisi yksi piste (tai joku muu pisteytys) jonnekkin HiScoreihin, jotka näkyis sitten jollain internetsivuilla haluamallaan nimimerkillä :)
vehkis91 [16.03.2010 20:32:12]LainaaMuokkaa
Ei millään pahalla, mutta mun mielestä limbo näyttää ihan saakelin sekaiselta verrattuna esim, pythoniin, c++, java , vb.net...

Ihan hieno esimerkki kyllä toi. :):)
lithi [23.04.2010 11:34:34]LainaaMuokkaa
juketsu kirjoitti:
Tähän olisi kiva saada lisä, että jokaisesta voitosta menisi yksi piste (tai joku muu pisteytys) jonnekkin HiScoreihin, jotka näkyis sitten jollain internetsivuilla haluamallaan nimimerkillä :)

Internetsivu olisi kyllä hyvä, tuon lisäksi vielä mahdollisesti ristinolla-liigat yms, hyvällä tuurilla ohjelmasi päätyy vielä MM-tason kilpailuksi!
Macro [13.07.2010 17:05:40]LainaaMuokkaa
tsuriga kirjoitti:
Koodivinkkien ohjeissa kyllä mainitaan kommentointivaatimukset.

Kun se kerran on hyväksytty tänne, niin se on ihan tarpeeksi hyvä.
Kray [20.07.2010 13:33:59]LainaaMuokkaa
Macro kirjoitti:
Kun se kerran on hyväksytty tänne, niin se on ihan tarpeeksi hyvä.

Sitä vaan ei hyväksytty.

ylläpito Antti Laaksonen, ulkoasu Otto Seiskari