Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Communication between objects

Tags:

raku

If I have Game Class, which has Player object and Board object, Game asks Player what are the coordinates, Player responds, then game checks Board for the coordinates and the result either Hit or Miss.

How can Game forward the result back to Player? so that Player uses the result to set the new coordinates.

I have created code sample below to explain more what i want to do

and also a link to the project here: https://github.com/hythm7/Battleship/tree/master


#!/usr/bin/env perl6

enum Result < Miss Hit >;

class Player {

  method fire ( ) {
    (^10).pick, (^10).pick
  }

}

class Board {
  has @.cell = [ +Bool.pick xx ^10 ] xx ^10;
}

class Game {
  has Board  $.board  = Board.new;
  has Player $!player = Player.new;

  method run ( ) {

    loop {

      my ($x, $y) = $!player.fire;

      if $!board.cell[$y][$x] {
        say Hit;
      }
      else {
        say Miss;
      }
      # How to forward the above result (Hit or Miss) back to the Player object? so
      # it can set $y, $x accordingly for the next call to $player.fire

      sleep 1;
    }
  }
}

my $game = Game.new;
$game.run;


like image 435
hythm Avatar asked Apr 18 '19 18:04

hythm


People also ask

What is communication in object?

Communication Objects define data structures that are communicated through services between components. The definition of communication objects requires primitive data types such as Int, Double, String, etc. and complex data types (i.e. composed data types).

What is object-oriented communication?

OOC is an attempt to balance simplicity and expressivity, and is sufficiently flexible to express data acquisition, control requests, alarm messages and error messages in a straightforward generic way. OOC supports dynamic creation of objects.

How do objects communicate with each other in object oriented programming?

Objects communicate with each other by passing messages. A message consists of the receiver object name and a method name with parameters. The methods of an object are invoked by means of messages.

How do objects communicate when performing a task?

We communicate with objects using methods. Methods are executable code within each object, for which an interface has been established. Sometimes the interface is only for the object itself. Other times it is an interface accessible by other objects.


2 Answers

Let's see. The main question here is a design one, I think, so let's go for it from this angle. I want to note beforehand that I will describe just a single example of the approach: there are a lot of ways to do it, and I am writing out the simplest I can imagine that works. Also, for the sake of simplicity, code that deals with synchronization, graceful termination and so on is omitted.

Firstly, you have a player to be a separate thing, but in your code it reacts only when it is called from the outside. When it looks like a natural approach when implementing turn-based games, we still want to have some kind of communication. What if a player leaves? What if there is some error condition?

And as you noted the server wants to notify the player about outcome of the game. It seems like we want to have a bi-directional messaging between our Server and Player. Of course, if there is a One Server -> Many Players relation, it is another deal, but we will keep it simple.

Let's prepare some boilerplate code:

# We will get to this `Start` later
enum EventType <Start Hit Miss>;

# A handy class to hold a position, and likely some other data in the future
class Position {
    has Int $.x;
    has Int $.y;
}

# A board
class Board {
    has @.cell = [ +Bool.pick xx ^10 ] xx ^10;
}

Now here is a server:

class Server {
    has Board $!board = Board.new;
    has Supply $.shots;
    has Channel $.player;

    method serve {
        react {
            # Whenever we get a shot coordinates, sent a Hit or Miss to the player
            whenever $!shots -> Position $pos {
                $!player.send($!board.cell[$pos.y][$pos.x] ?? Hit !! Miss);
                # Don't forget to say "I am ready for new events" for the client
                $!player.send(Start);
            }
            # Somebody should start first, and it will be a Server...
            $!player.send(Start);
        }
    }
}

It has a board, and two other attributes - a Supply $.shots and a Channel $.player. If we want to tell something to our player, we are sending a message to the channel. At the same time, we want to know what player wants us to know, so we are listening on everything that comes from our $!shots async stream of values. The serve method just does our logic - reacts to player's events.

Now to our Player:

class Player {
    has Channel $.server;
    has Supply $.events;

    method play {
        react {
            whenever $!events {
                when Start {
                    # Here can be user's input
                    # Simulate answer picking
                    sleep 1;
                    $!server.send: Position.new(x => (^10).pick, y => (^10).pick);
                    # Can be something like:
                    # my ($x, $y) = get.Int, get.Int;
                    # $!server.send: Position.new(:$x, :$y);

                }
                when Hit {
                    say "I hit that! +1 gold coin!";
                }
                when Miss {
                    say "No, that's a miss... -1 bullet!"
                }
            }
        }
    }
}

Player has a Channel and a Supply too, as we want a bi-directional relationship. $!server is used to send actions to the server and $!events provides us a stream of events back.

The play method is implemented this way: if the server says that it is ok with our action, we can made our move, if not - we are basically waiting, and when a Hit or Miss event appears, we react to it.

Now we want to tie those two together:

class Game {
    has Server $!server;
    has Player $!player;

    method start {
        my $server-to-player = Channel.new;
        my $player-to-server = Channel.new;

        $!server = Server.new(player => $server-to-player,
                              shots => $player-to-server.Supply);
        $!player = Player.new(server => $player-to-server,
                              events => $server-to-player.Supply);

        start $!server.serve;
        sleep 1;
        $!player.play;
    }
}.new.start;

Firstly, we are creating two channels with self-contained names. Then we create both Server and Player with those channels reversed: player can send to the first one and listen to the second one, server can send to the second one and listen to the first one.

As react is a blocking construct, we cannot run both methods in the same thread, so we start a server in another thread. Then we sleep 1 second to make sure it serves us(that's a hack to avoid negotiation code in this already pretty long answer), and start the player (be it emulation or a real input, you can try both).

Modifying the handlers and the data types sent between Player and Server you can build more complex examples yourself.

like image 193
Takao Avatar answered Oct 18 '22 23:10

Takao


One way to do it is to add a Board to the player. If you make it $.board then you get at least a public read accessor which you'll be able to use in the Game class and if you make it is rw you'll get a write accessor so you can just write it.

So, add the Board to Player:

class Player {
  has Board  $.board is rw = Board.new;

  method fire ( ) {
    (^10).pick, (^10).pick
}

(And for that to compile you'll need to move the Board class declaration above Player otherwise you'll get a Type 'Board' is not declared error.)

And now you can add a line like this somewhere in the Board class:

  $!player.board.cell[$y][$x] = Hit; # or Miss

Also, you need to record one of three states in the cells of the player's board, not two -- Hit, Miss, or unknown. Maybe add Unknown to the enum and initialize the player's board with Unknowns.

like image 22
raiph Avatar answered Oct 18 '22 22:10

raiph