UPK6W3YC62YCWHXNGGK4PDJBPK5JEZW5R3HYGWDJSVF5ULKFQERAC LMHTQTH7J7K6ZBBZOOAMMYFCVPCWGUUKDQOTP27NIV7Y7H3ZXPXQC RKTDDNSGNOB7K27XI6ZBRXHCBOAMAKM667BUQIQSVZ2YJED2YCPQC BFHHGVOJAAJBFVROYZ42XNVOFOWK7KQKWTYZLF3P5HGA7BHLJXNQC 6XO5SUSOGLDRQAKXHBFQTQPOQ7WMVXIVFVDABQFOQTHKBBTCKD6AC RDDAB3MJC22EXAXNO4P537HCJGXMXJCRQH75VCTT4VUM2QRRQBOQC U3JHTSEMJLXNKOMAQJQPZKRW6GRPHYLG6DK53XVADIETKVCS2MFQC DYHBLNX63S326PH2KNHIBBQ27L2LXRHUVU3SNL3KRITV5RZNWA5AC 5AAIYEWRNV2H5226AFCA3SNM44P6ENVRDXGOYENPSCJV6L2N62AAC dependencies:uuid: ^3.0.7
import 'package:dartmcts/net.dart';import 'package:shelf/shelf.dart';import 'package:shelf/shelf_io.dart' as shelf_io;import 'dart:convert';import 'package:uuid/uuid.dart';Function? gameHandler;void serve(TrainableInterface Function() trainer) async {var handler =const Pipeline().addMiddleware(logRequests()).addHandler(_handleRequest);gameHandler = () => trainer.call();var server = await shelf_io.serve(handler, 'localhost', 5000);print('Serving at http://${server.address.host}:${server.port}');}Map<String, TrainableInterface> gamesInProgress = {};Future<Response> _handleRequest(Request request) async {if (request.url.path == "newgame") {TrainableInterface game = gameHandler?.call();var uuid = const Uuid();String id = uuid.v4().toString();gamesInProgress[id] = game;return Response.ok(json.encode({"id": id,"player_count": game.playerCount,"action_space_size": game.actionSpaceSize,"observation_space_size": game.observationSpaceSize,"current_player": game.currentPlayer,"observation": game.observation(),"legal_actions": game.legalActions()}),headers: {'Content-Type': 'application/json'});} else {var pieces = request.url.path.split('/');assert(pieces[0] == "step");var id = pieces[1];var action = json.decode(await request.readAsString())['action'];TrainableInterface? game = gamesInProgress[id]!;var gameResponse = game.step(action);var stepResponse = json.encode({"observation": game.observation(),"legal_actions": game.legalActions(),"next_player": game.currentPlayer,"reward": gameResponse.reward,"done": gameResponse.done});if (gameResponse.done) {// free memory for finished gamesgamesInProgress.remove(id);game = null;}return Response.ok(stepResponse,headers: {'Content-Type': 'application/json'});}}
return l;}List<double> encodeGame(TicTacToeGame game) {List<double> l = [];List<double> myLocations = List.filled(9, 0);List<double> opponentLocations = List.filled(9, 0);game.board.asMap().forEach((i, player) {if (player == null) return;if (player == game.currentPlayer) {myLocations[i] = 1;} else {opponentLocations[i] = 1;}});l.addAll(myLocations);l.addAll(opponentLocations);// legalMoves must always be appended to the observationl.addAll(legalMoves(game));return l;
class TicTacToeNNInterface extends TrainableInterface {TicTacToeGame game = TicTacToeGame.newGame() as TicTacToeGame;@overrideint get playerCount => 2;@overrideint get currentPlayer => game.currentPlayer == TicTacToePlayer.O ? 0 : 1;@overrideList<double> legalActions() {return legalMoves(game);}@overrideList<double> observation() {return encodeGame(game);}@overrideStepResponse step(int move) {bool done = false;List<double> reward = List.filled(playerCount, 0.0);game = game.cloneAndApplyMove(move, null);if (game.getMoves().length == 0 || game.winner != null) {done = true;if (game.winner == null) {// tiereward = [0, 0];} else {// clear winner - the winner gets 1.0 - everyone else gets -1.0 rewardreward = [-1.0, -1.0];reward[game.winner! == TicTacToePlayer.O ? 0 : 1] = 1.0;}}return StepResponse(done: done,reward: reward,);}}
/// Initialize and optionally set a value in a one-hot arrayList<double> initOneHot(int length, {double filler = 0, int? value}) {var l = List<double>.filled(length, filler);if (value != null) {l[value] = 1;}return l;}/// A tuple for move and scoreclass MoveScore<Move> {Move move;double score;MoveScore(this.move, this.score);@overrideString toString() {return 'MoveScore(score: $score, move: $move)';}}/// A response for a moveclass StepResponse {bool done = false;List<double> reward = [];StepResponse({required this.done, required this.reward});}abstract class TrainableInterface {int get actionSpaceSize {return legalActions().length;}int get observationSpaceSize {return observation().length - legalActions().length;}late int playerCount;late int currentPlayer;List<double> observation();List<double> legalActions();StepResponse step(int move);}
import 'package:dartmcts/trainingserver.dart';import 'package:dartmcts/tictactoe.dart';void main() {serve(() => TicTacToeNNInterface());}