Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import 'dart:async';
- import 'dart:io';
- import 'dart:math';
- import 'package:aqueduct/aqueduct.dart';
- Future main() async {
- final app = Application<Hangman3App>()..options.port = 8888;
- await app.start(numberOfInstances: 1, consoleLogging: true);
- }
- const DICTIONARY = [
- 'adventurous',
- 'courageous',
- 'extramundane',
- 'generous',
- 'intransigent',
- 'sympathetic',
- 'vagarious',
- 'witty',
- ];
- bool hangman(String word, List<String> letters) {
- return word == knownWord(word, letters);
- }
- String knownWord(String word, List<String> letters) {
- return word.split('').map((_) => letters.contains(_) ? _ : '_').join();
- }
- class HangmanStatus {
- static const String win = 'win';
- static const String lose = 'lose';
- static const String inProgress = 'in-progress';
- }
- class Hangman extends Object {
- String id;
- int life;
- List<String> selectedLetters;
- int secretWordLength;
- String knownLetters;
- String status;
- String secretWord;
- Hangman({
- this.id,
- this.life,
- this.selectedLetters,
- this.secretWordLength,
- this.knownLetters,
- this.status,
- this.secretWord,
- });
- @override
- String toString() {
- return 'Hangman{id: $id, life: $life, selectedLetters: $selectedLetters, secretWordLength: $secretWordLength, knownLetters: $knownLetters, status: $status, secretWord: $secretWord}';
- }
- factory Hangman.random({String id}) {
- var word = DICTIONARY[Random().nextInt(DICTIONARY.length)];
- // <5:1, <10:2, ...
- var selected = <String>[];
- var selectedLength = (word.length * 0.2).ceil().toInt();
- var pool = Set.from(word.split('')).toList();
- while (selected.length < selectedLength && pool.isNotEmpty) {
- selected.add(pool.removeAt(Random().nextInt(pool.length)));
- }
- return Hangman.select(
- id: id,
- selectedLetters: selected,
- word: word,
- );
- }
- factory Hangman.select(
- {String id, String word, List<String> selectedLetters}) {
- return Hangman(
- id: id,
- life: 7,
- selectedLetters: selectedLetters,
- secretWordLength: word.length,
- knownLetters: knownWord(word, selectedLetters),
- secretWord: word,
- status: HangmanStatus.inProgress,
- );
- }
- play(String letter) {
- if (this.selectedLetters.contains(letter)) {
- // do nothing for already selected letters
- // seem like bug for game
- return;
- }
- this.selectedLetters.add(letter);
- var known = knownWord(this.secretWord, this.selectedLetters);
- if (hangman(this.secretWord, this.selectedLetters)) {
- this.status = HangmanStatus.win;
- this.knownLetters = known;
- } else if (known != this.knownLetters) {
- this.knownLetters = known;
- } else if (this.life > 1) {
- this.life = this.life - 1;
- } else {
- this.status = HangmanStatus.lose;
- this.life = 0;
- }
- }
- void timeout() {
- if (this.life > 1) {
- this.life = this.life - 1;
- } else {
- this.status = HangmanStatus.lose;
- this.life = 0;
- }
- }
- bool get inProgress => this.status == HangmanStatus.inProgress;
- @override
- int get hashCode {
- return id.hashCode ^
- life.hashCode ^
- selectedLetters.hashCode ^
- secretWordLength.hashCode ^
- knownLetters.hashCode ^
- secretWord.hashCode ^
- status.hashCode;
- }
- @override
- bool operator ==(other) {
- return ((id == other.id) &&
- (life == other.life) &&
- (selectedLetters == other.selectedLetters) &&
- (secretWordLength == other.secretWordLength) &&
- (knownLetters == other.knownLetters) &&
- (secretWord == other.secretWord) &&
- (status == other.status));
- }
- }
- class HangmanDao extends Serializable {
- Hangman hangman;
- HangmanDao(this.hangman);
- factory HangmanDao.random({String id}) {
- return HangmanDao(Hangman.random(id: id));
- }
- @override
- String toString() {
- return 'HangmanDao{hangman: $hangman}';
- }
- @override
- Map<String, dynamic> asMap() {
- return {
- 'id': hangman.id,
- 'life': hangman.life,
- 'selectedLetters': hangman.selectedLetters,
- 'secretWordLength': hangman.secretWordLength,
- 'knownLetters': hangman.knownLetters,
- 'status': hangman.status,
- };
- }
- @override
- void readFromMap(Map<String, dynamic> requestBody) {
- hangman = Hangman(
- id: requestBody['id'],
- life: requestBody['life'],
- selectedLetters: requestBody['selectedLetters'],
- secretWordLength: requestBody['secretWordLength'],
- knownLetters: requestBody['knownLetters'],
- status: requestBody['status'],
- secretWord: requestBody['secretWord'],
- );
- }
- play(String letter) => hangman.play(letter);
- timeout() => hangman.timeout();
- bool get inProgress => hangman.inProgress;
- }
- class Hangman3Game extends Serializable {
- // Output stream
- final _outputController = StreamController<String>();
- Stream<String> get stream => _outputController.stream;
- // Input Stream
- final _inputController = StreamController<String>();
- Sink<String> get sink => _inputController.sink;
- Stream<String> get _inputStream => _inputController.stream;
- // Timer Stream
- final _timerController = StreamController<int>();
- Sink<int> get tick => _timerController.sink;
- Stream<int> get _timerStream => _timerController.stream;
- // remember state
- HangmanDao _game;
- HangmanDao get game => _game;
- Timer _timer;
- int _count = 0;
- static const COUNT_MAX = 5;
- static const GAME_EVENT = {
- HangmanStatus.inProgress: 'time-spent',
- HangmanStatus.win: 'game-complete',
- HangmanStatus.lose: 'game-over',
- };
- Hangman3Game(int i) {
- _game = HangmanDao.random(id: 'gameId-$i');
- _inputStream.listen(_play);
- _timerStream.listen(_tick);
- }
- void _play(String letter) {
- print('play with $letter');
- if (!_game.inProgress) return;
- _game.play(letter);
- _trigger(); // start after player send first letter
- }
- void _timeout() {
- print('timeout');
- if (!_game.inProgress) return;
- _game.timeout();
- _trigger();
- }
- void _trigger() {
- _response();
- if (_game.inProgress) {
- _start();
- } else {
- _end();
- }
- }
- void _response() {
- var id = DateTime.now().toIso8601String();
- var timeLeft = COUNT_MAX - _count;
- var lifeLeft = game.hangman.life;
- var event = GAME_EVENT[game.hangman.status];
- var d =
- 'id: "$id"\n, event: "$event"\n, data: {timeLeft: $timeLeft, lifeLeft: $lifeLeft}\n\n';
- _outputController.add(d);
- }
- void _start() {
- _timer?.cancel();
- _timer = Timer.periodic(Duration(seconds: 1), (Timer timer) {
- tick.add(timer.tick);
- });
- }
- void _end() {
- _timer?.cancel();
- _outputController.close();
- }
- void _tick(int count) {
- print('tick $count');
- _count = count;
- if (count < COUNT_MAX) {
- _response();
- } else {
- _timeout();
- }
- }
- dispose() {
- _inputController?.close();
- _outputController?.close();
- _timerController?.close();
- _timer?.cancel();
- }
- @override
- Map<String, dynamic> asMap() {
- return _game.asMap();
- }
- @override
- void readFromMap(Map<String, dynamic> requestBody) {
- _game.readFromMap(requestBody);
- }
- }
- class Hangman3App extends ApplicationChannel {
- var pattern = '/hangman[/:id[/:letter]]';
- var controller = Hangman3Controller();
- @override
- Controller get entryPoint {
- return Router()..route(pattern).link(() => controller);
- }
- }
- class Hangman3Controller extends ResourceController {
- var _games = <Hangman3Game>[
- Hangman3Game(1),
- Hangman3Game(2),
- ];
- Hangman3Game _getGame(String id) {
- final game =
- _games.firstWhere((_) => _.game.hangman.id == id, orElse: () => null);
- print('got=$game');
- return game;
- }
- _newGame() {
- final game = HangmanDao.random(id: 'gameId-${_games.length + 1}');
- print('new=$game');
- return game;
- }
- _updateGame(Hangman3Game game) {
- _games[_games
- .indexWhere((_) => _.game.hangman.id == game.game.hangman.id)] = game;
- }
- @Operation.get()
- Future<Response> getAllGames() async {
- return Response.ok(_games);
- }
- @Operation.get('id')
- Future<Response> getGame() async {
- final id = request.path.variables['id'];
- var game = _getGame(id);
- if (game == null) {
- return Response.notFound();
- }
- return Response.ok(game);
- }
- @Operation.get('id', 'letter')
- Future<Response> getGameTimerEvent() async {
- final id = request.path.variables['id'];
- final letter = request.path.variables['letter'];
- var game = _getGame(id);
- if (game == null) {
- return Response.notFound();
- }
- if (letter != 'timer') return Response.ok(game);
- return Response.ok(game.stream)
- ..bufferOutput = false
- ..contentType = new ContentType('text', 'event-stream', charset: 'utf-8');
- }
- @Operation.post()
- Future<Response> createGame() async {
- var game = _newGame();
- return Response.ok(game);
- }
- @Operation.put('id', 'letter')
- Future<Response> playGame() async {
- final id = request.path.variables['id'];
- final letter = request.path.variables['letter'];
- var game = _getGame(id);
- if (game == null) {
- return Response.notFound();
- }
- _updateGame(game..sink.add(letter));
- return Response.ok(null);
- }
- }
Add Comment
Please, Sign In to add comment