Связь между объектами - PullRequest
       9

Связь между объектами

8 голосов
/ 18 апреля 2019

Если у меня есть Game Class, в котором есть объект Player и объект Board, игра спрашивает игрока, каковы координаты, Player отвечает, а затем игра проверяет Board на наличие координат и результат: Hit или Miss.

HowМожет ли игра переслать результат обратно игроку?так что Player использует результат для установки новых координат.

Я создал пример кода ниже, чтобы объяснить больше, что я хочу сделать

, а также ссылку на проект здесь: 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;


Ответы [ 2 ]

12 голосов
/ 18 апреля 2019

Посмотрим. Я думаю, что главный вопрос - это вопрос дизайна, так что давайте подойдем к этому с этой точки зрения. Заранее хочу отметить, что я опишу лишь один пример этого подхода: есть много способов сделать это, и я пишу самый простой, который я могу себе представить, который работает. Кроме того, для простоты код, который имеет дело с синхронизацией, постепенным завершением и т. Д., Опущен.

Во-первых, у вас есть игрок, который должен быть отдельным предметом, но в вашем коде он реагирует только тогда, когда он вызывается извне. Когда это выглядит как естественный подход при реализации пошаговых игр, мы все равно хотим иметь какое-то общение. Что делать, если игрок уходит? Что делать, если есть какое-то условие ошибки?

И, как вы отметили, сервер хочет уведомить игрока о результатах игры. Кажется, что мы хотим иметь двунаправленный обмен сообщениями между нашим Сервером и Игроком. Конечно, если существует отношение «Один сервер -> Много игроков», это еще одна сделка, но мы будем простыми.

Давайте подготовим примерный код:

# 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;
}

Теперь вот сервер:

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);
        }
    }
}

У него есть доска и два других атрибута - снабжение $.shots и Канал $.player. Если мы хотим что-то сказать нашему плееру, мы отправляем сообщение на канал. В то же время, мы хотим знать, что игрок хочет, чтобы мы знали, поэтому мы слушаем все, что исходит из нашего $!shots асинхронного потока значений. Метод serve просто выполняет нашу логику - реагирует на события игрока.

Теперь нашему игроку:

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!"
                }
            }
        }
    }
}

У игрока также есть канал и запас, так как мы хотим двунаправленных отношений. $! server используется для отправки действий на сервер, а $! events предоставляет нам поток событий назад.

Метод play реализован следующим образом: если сервер говорит, что все в порядке с нашими действиями, мы можем сделать наш ход, если нет - мы в основном ожидаем, и когда появляется событие Hit или Miss, мы реагируем к этому.

Теперь мы хотим связать эти два:

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;

Во-первых, мы создаем два канала с автономными именами. Затем мы создаем сервер и проигрыватель с обратными каналами: игрок может отправить первый и прослушать второй, сервер может отправить второй и прослушать первый.

Поскольку react является блокирующей конструкцией, мы не можем запустить оба метода в одном потоке, поэтому мы start сервер в другом потоке. Затем мы спим 1 секунду, чтобы убедиться, что он нам подходит (это хак, чтобы избежать кода согласования в этом уже довольно длинном ответе), и запускаем проигрыватель (будь то эмуляция или реальный ввод, вы можете попробовать оба).

Изменяя обработчики и типы данных, передаваемые между проигрывателем и сервером, вы можете создавать более сложные примеры самостоятельно.

4 голосов
/ 18 апреля 2019

Один из способов сделать это - добавить Board к игроку.Если вы сделаете его $.board, вы получите по крайней мере общедоступный метод чтения, который вы сможете использовать в классе Game, а если вы сделаете его is rw, вы получите метод записи, так что вы можете просто писатьit.

Итак, добавьте Board к Player:

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

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

(И для этого для компиляции вам нужно переместить объявление класса Board выше Player в противном случае вы получите Type 'Board' is not declared ошибку.)

И теперь вы можете добавить строку где-то в классе Board:

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

Кроме того, вам нужнозапишите одно из трех состояний в ячейках игрового поля, а не два - Hit, Miss или неизвестно.Возможно добавьте Unknown к перечислению и инициализируйте доску игрока с Unknown s.

...