Guest User

Untitled

a guest
Oct 19th, 2018
88
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.07 KB | None | 0 0
  1. import 'dart:async';
  2. import 'dart:io';
  3. import 'dart:math';
  4.  
  5. import 'package:aqueduct/aqueduct.dart';
  6.  
  7. Future main() async {
  8. final app = Application<Hangman3App>()..options.port = 8888;
  9.  
  10. await app.start(numberOfInstances: 1, consoleLogging: true);
  11. }
  12.  
  13. const DICTIONARY = [
  14. 'adventurous',
  15. 'courageous',
  16. 'extramundane',
  17. 'generous',
  18. 'intransigent',
  19. 'sympathetic',
  20. 'vagarious',
  21. 'witty',
  22. ];
  23.  
  24. bool hangman(String word, List<String> letters) {
  25. return word == knownWord(word, letters);
  26. }
  27.  
  28. String knownWord(String word, List<String> letters) {
  29. return word.split('').map((_) => letters.contains(_) ? _ : '_').join();
  30. }
  31.  
  32. class HangmanStatus {
  33. static const String win = 'win';
  34. static const String lose = 'lose';
  35. static const String inProgress = 'in-progress';
  36. }
  37.  
  38. class Hangman extends Object {
  39. String id;
  40. int life;
  41. List<String> selectedLetters;
  42. int secretWordLength;
  43. String knownLetters;
  44. String status;
  45. String secretWord;
  46.  
  47. Hangman({
  48. this.id,
  49. this.life,
  50. this.selectedLetters,
  51. this.secretWordLength,
  52. this.knownLetters,
  53. this.status,
  54. this.secretWord,
  55. });
  56.  
  57. @override
  58. String toString() {
  59. return 'Hangman{id: $id, life: $life, selectedLetters: $selectedLetters, secretWordLength: $secretWordLength, knownLetters: $knownLetters, status: $status, secretWord: $secretWord}';
  60. }
  61.  
  62. factory Hangman.random({String id}) {
  63. var word = DICTIONARY[Random().nextInt(DICTIONARY.length)];
  64. // <5:1, <10:2, ...
  65. var selected = <String>[];
  66. var selectedLength = (word.length * 0.2).ceil().toInt();
  67. var pool = Set.from(word.split('')).toList();
  68. while (selected.length < selectedLength && pool.isNotEmpty) {
  69. selected.add(pool.removeAt(Random().nextInt(pool.length)));
  70. }
  71. return Hangman.select(
  72. id: id,
  73. selectedLetters: selected,
  74. word: word,
  75. );
  76. }
  77.  
  78. factory Hangman.select(
  79. {String id, String word, List<String> selectedLetters}) {
  80. return Hangman(
  81. id: id,
  82. life: 7,
  83. selectedLetters: selectedLetters,
  84. secretWordLength: word.length,
  85. knownLetters: knownWord(word, selectedLetters),
  86. secretWord: word,
  87. status: HangmanStatus.inProgress,
  88. );
  89. }
  90.  
  91. play(String letter) {
  92. if (this.selectedLetters.contains(letter)) {
  93. // do nothing for already selected letters
  94. // seem like bug for game
  95. return;
  96. }
  97. this.selectedLetters.add(letter);
  98. var known = knownWord(this.secretWord, this.selectedLetters);
  99. if (hangman(this.secretWord, this.selectedLetters)) {
  100. this.status = HangmanStatus.win;
  101. this.knownLetters = known;
  102. } else if (known != this.knownLetters) {
  103. this.knownLetters = known;
  104. } else if (this.life > 1) {
  105. this.life = this.life - 1;
  106. } else {
  107. this.status = HangmanStatus.lose;
  108. this.life = 0;
  109. }
  110. }
  111.  
  112. void timeout() {
  113. if (this.life > 1) {
  114. this.life = this.life - 1;
  115. } else {
  116. this.status = HangmanStatus.lose;
  117. this.life = 0;
  118. }
  119. }
  120.  
  121. bool get inProgress => this.status == HangmanStatus.inProgress;
  122.  
  123. @override
  124. int get hashCode {
  125. return id.hashCode ^
  126. life.hashCode ^
  127. selectedLetters.hashCode ^
  128. secretWordLength.hashCode ^
  129. knownLetters.hashCode ^
  130. secretWord.hashCode ^
  131. status.hashCode;
  132. }
  133.  
  134. @override
  135. bool operator ==(other) {
  136. return ((id == other.id) &&
  137. (life == other.life) &&
  138. (selectedLetters == other.selectedLetters) &&
  139. (secretWordLength == other.secretWordLength) &&
  140. (knownLetters == other.knownLetters) &&
  141. (secretWord == other.secretWord) &&
  142. (status == other.status));
  143. }
  144. }
  145.  
  146. class HangmanDao extends Serializable {
  147. Hangman hangman;
  148.  
  149. HangmanDao(this.hangman);
  150.  
  151. factory HangmanDao.random({String id}) {
  152. return HangmanDao(Hangman.random(id: id));
  153. }
  154.  
  155. @override
  156. String toString() {
  157. return 'HangmanDao{hangman: $hangman}';
  158. }
  159.  
  160. @override
  161. Map<String, dynamic> asMap() {
  162. return {
  163. 'id': hangman.id,
  164. 'life': hangman.life,
  165. 'selectedLetters': hangman.selectedLetters,
  166. 'secretWordLength': hangman.secretWordLength,
  167. 'knownLetters': hangman.knownLetters,
  168. 'status': hangman.status,
  169. };
  170. }
  171.  
  172. @override
  173. void readFromMap(Map<String, dynamic> requestBody) {
  174. hangman = Hangman(
  175. id: requestBody['id'],
  176. life: requestBody['life'],
  177. selectedLetters: requestBody['selectedLetters'],
  178. secretWordLength: requestBody['secretWordLength'],
  179. knownLetters: requestBody['knownLetters'],
  180. status: requestBody['status'],
  181. secretWord: requestBody['secretWord'],
  182. );
  183. }
  184.  
  185. play(String letter) => hangman.play(letter);
  186.  
  187. timeout() => hangman.timeout();
  188.  
  189. bool get inProgress => hangman.inProgress;
  190. }
  191.  
  192. class Hangman3Game extends Serializable {
  193. // Output stream
  194. final _outputController = StreamController<String>();
  195.  
  196. Stream<String> get stream => _outputController.stream;
  197.  
  198. // Input Stream
  199. final _inputController = StreamController<String>();
  200.  
  201. Sink<String> get sink => _inputController.sink;
  202.  
  203. Stream<String> get _inputStream => _inputController.stream;
  204.  
  205. // Timer Stream
  206. final _timerController = StreamController<int>();
  207.  
  208. Sink<int> get tick => _timerController.sink;
  209.  
  210. Stream<int> get _timerStream => _timerController.stream;
  211.  
  212. // remember state
  213. HangmanDao _game;
  214.  
  215. HangmanDao get game => _game;
  216.  
  217. Timer _timer;
  218.  
  219. int _count = 0;
  220.  
  221. static const COUNT_MAX = 5;
  222. static const GAME_EVENT = {
  223. HangmanStatus.inProgress: 'time-spent',
  224. HangmanStatus.win: 'game-complete',
  225. HangmanStatus.lose: 'game-over',
  226. };
  227.  
  228. Hangman3Game(int i) {
  229. _game = HangmanDao.random(id: 'gameId-$i');
  230. _inputStream.listen(_play);
  231. _timerStream.listen(_tick);
  232. }
  233.  
  234. void _play(String letter) {
  235. print('play with $letter');
  236. if (!_game.inProgress) return;
  237. _game.play(letter);
  238. _trigger(); // start after player send first letter
  239. }
  240.  
  241. void _timeout() {
  242. print('timeout');
  243. if (!_game.inProgress) return;
  244. _game.timeout();
  245. _trigger();
  246. }
  247.  
  248. void _trigger() {
  249. _response();
  250. if (_game.inProgress) {
  251. _start();
  252. } else {
  253. _end();
  254. }
  255. }
  256.  
  257. void _response() {
  258. var id = DateTime.now().toIso8601String();
  259. var timeLeft = COUNT_MAX - _count;
  260. var lifeLeft = game.hangman.life;
  261. var event = GAME_EVENT[game.hangman.status];
  262. var d =
  263. 'id: "$id"\n, event: "$event"\n, data: {timeLeft: $timeLeft, lifeLeft: $lifeLeft}\n\n';
  264. _outputController.add(d);
  265. }
  266.  
  267. void _start() {
  268. _timer?.cancel();
  269. _timer = Timer.periodic(Duration(seconds: 1), (Timer timer) {
  270. tick.add(timer.tick);
  271. });
  272. }
  273.  
  274. void _end() {
  275. _timer?.cancel();
  276. _outputController.close();
  277. }
  278.  
  279. void _tick(int count) {
  280. print('tick $count');
  281. _count = count;
  282. if (count < COUNT_MAX) {
  283. _response();
  284. } else {
  285. _timeout();
  286. }
  287. }
  288.  
  289. dispose() {
  290. _inputController?.close();
  291. _outputController?.close();
  292. _timerController?.close();
  293. _timer?.cancel();
  294. }
  295.  
  296. @override
  297. Map<String, dynamic> asMap() {
  298. return _game.asMap();
  299. }
  300.  
  301. @override
  302. void readFromMap(Map<String, dynamic> requestBody) {
  303. _game.readFromMap(requestBody);
  304. }
  305. }
  306.  
  307. class Hangman3App extends ApplicationChannel {
  308. var pattern = '/hangman[/:id[/:letter]]';
  309. var controller = Hangman3Controller();
  310.  
  311. @override
  312. Controller get entryPoint {
  313. return Router()..route(pattern).link(() => controller);
  314. }
  315. }
  316.  
  317. class Hangman3Controller extends ResourceController {
  318. var _games = <Hangman3Game>[
  319. Hangman3Game(1),
  320. Hangman3Game(2),
  321. ];
  322.  
  323. Hangman3Game _getGame(String id) {
  324. final game =
  325. _games.firstWhere((_) => _.game.hangman.id == id, orElse: () => null);
  326. print('got=$game');
  327. return game;
  328. }
  329.  
  330. _newGame() {
  331. final game = HangmanDao.random(id: 'gameId-${_games.length + 1}');
  332. print('new=$game');
  333. return game;
  334. }
  335.  
  336. _updateGame(Hangman3Game game) {
  337. _games[_games
  338. .indexWhere((_) => _.game.hangman.id == game.game.hangman.id)] = game;
  339. }
  340.  
  341. @Operation.get()
  342. Future<Response> getAllGames() async {
  343. return Response.ok(_games);
  344. }
  345.  
  346. @Operation.get('id')
  347. Future<Response> getGame() async {
  348. final id = request.path.variables['id'];
  349. var game = _getGame(id);
  350. if (game == null) {
  351. return Response.notFound();
  352. }
  353. return Response.ok(game);
  354. }
  355.  
  356. @Operation.get('id', 'letter')
  357. Future<Response> getGameTimerEvent() async {
  358. final id = request.path.variables['id'];
  359. final letter = request.path.variables['letter'];
  360. var game = _getGame(id);
  361. if (game == null) {
  362. return Response.notFound();
  363. }
  364. if (letter != 'timer') return Response.ok(game);
  365. return Response.ok(game.stream)
  366. ..bufferOutput = false
  367. ..contentType = new ContentType('text', 'event-stream', charset: 'utf-8');
  368. }
  369.  
  370. @Operation.post()
  371. Future<Response> createGame() async {
  372. var game = _newGame();
  373. return Response.ok(game);
  374. }
  375.  
  376. @Operation.put('id', 'letter')
  377. Future<Response> playGame() async {
  378. final id = request.path.variables['id'];
  379. final letter = request.path.variables['letter'];
  380. var game = _getGame(id);
  381. if (game == null) {
  382. return Response.notFound();
  383. }
  384. _updateGame(game..sink.add(letter));
  385. return Response.ok(null);
  386. }
  387. }
Add Comment
Please, Sign In to add comment