// Awake is called when the script instance is being loaded private void Awake() { // Set result to none when the match starts Result = Winner.None; // Instantiate the unity event for notifying end of match MatchOver = new UnityEvent(); // Get reference to the match data provider matchData = GetComponentInParent <IMatchDataProvider>(); // Get reference to the match configuration matchConfig = GetComponentInParent <IMatchConfig>(); // Get reference to the game board board = matchData.Board; // Instantiate the array where to place the solution solution = new Pos[board.piecesInSequence]; // Get reference to the match view object view = GameObject.Find("UI")?.GetComponent <MatchView>(); // Get the AI time limit as a native C# TimeSpan aiTimeLimit = new TimeSpan( (long)(matchConfig.TimeLimitMillis * TimeSpan.TicksPerMillisecond)); // Instantiate the stopwatch stopwatch = new Stopwatch(); // Set cancellation stopwatch to null cancellationStopwatch = null; }
/// <summary> /// Sets up a new match. /// </summary> /// <param name="matchConfig">Match configuration.</param> /// <param name="matchData">Match data.</param> public MatchController( IMatchConfig matchConfig, IMatchDataProvider matchData) { // Keep a reference to the match config this.matchConfig = matchConfig; // Keep a reference to the match data this.matchData = matchData; // Initialize the solution array solution = new Pos[matchConfig.WinSequence]; // Initialize the cancellation token ts = new CancellationTokenSource(); // Instantiate the stopwatch stopwatch = new Stopwatch(); // Get a reference to the game board board = matchData.Board; // Fetch times from match options timeLimitMillis = matchConfig.TimeLimitMillis; minMoveTimeMillis = matchConfig.MinMoveTimeMillis; }
// Outputs the final standings and results in Markdown format // Called after the session is over private void AfterSession(ISessionDataProvider sessionData) { using (StreamWriter outFile = new StreamWriter(StandingsFile)) { int i = 0; IMatchConfig cfg = sessionData.MatchConfig; // Header and update time outFile.WriteLine("# Standings"); outFile.WriteLine(); outFile.WriteLine($"Last update: {DateTime.Now.ToString("R")}"); outFile.WriteLine(); outFile.WriteLine("## Configuration"); outFile.WriteLine(); outFile.WriteLine("| Parameter | Value |"); outFile.WriteLine("|:-------------- | ----------------: |"); outFile.WriteLine($"| Rows | {cfg.Rows} |"); outFile.WriteLine($"| Cols | {cfg.Cols} |"); outFile.WriteLine($"| Win sequence | {cfg.WinSequence} |"); outFile.WriteLine($"| Round pieces | {cfg.RoundPiecesPerPlayer} |"); outFile.WriteLine($"| Square pieces | {cfg.SquarePiecesPerPlayer} |"); outFile.WriteLine($"| Time limit | {cfg.TimeLimitMillis}ms |"); outFile.WriteLine(); // Classification outFile.WriteLine("## Classification"); outFile.WriteLine(); outFile.WriteLine("| Pos. | AI Thinker | Points |"); outFile.WriteLine("|:----:| ---------- | -----: |"); foreach (KeyValuePair <string, int> tp in sessionData.Standings) { outFile.WriteLine($"| {++i} | {tp.Key} | {tp.Value} |"); } outFile.WriteLine(); // Results outFile.WriteLine("## Results"); outFile.WriteLine(); outFile.WriteLine("_Winner, if any, shown in bold_"); outFile.WriteLine(); outFile.WriteLine("| White | Red | Details |"); outFile.WriteLine("| -----:|:------- | :-----: |"); foreach (KeyValuePair <Match, Winner> mw in sessionData.Results) { string white = $"`{mw.Key.thinkerWhite}`"; string red = $"`{mw.Key.thinkerRed}`"; if (mw.Value == Winner.White) { white = $"**{white}**"; } else if (mw.Value == Winner.Red) { red = $"**{red}**"; } outFile.WriteLine("| {0} | {1} | [+]({2}/{3}) |", white, red, PlayoutsFolder, MatchPlayoutFileName(mw.Key)); } } }
// //////////////////////////////// // // Methods for listening to matches // // //////////////////////////////// // // Renders information about the match about to start private void MatchStart( IMatchConfig matchConfig, IList <string> thinkerNames) { // Show who's playing Console.WriteLine( $"=> {thinkerNames[0]} (White) vs {thinkerNames[1]} (Red) <=\n"); // Show piece legend Console.WriteLine("\tw - White Round"); Console.WriteLine("\tW - White Square"); Console.WriteLine("\tr - Red Round"); Console.WriteLine("\tR - Red Square"); Console.WriteLine(); }
/// <summary> /// Create a new session controller. /// </summary> /// <param name="matchConfig">Match configuration.</param> /// <param name="sessionConfig">Session configuration.</param> /// <param name="thinkerPrototypes"> /// List of thinker prototypes for thinkers participating in /// this session. /// </param> /// <param name="thinkerListeners">List of thinker listeners.</param> /// <param name="matchListeners">List of match listeners.</param> /// <param name="sessionListeners">List of session listeners.</param> public SessionController( IMatchConfig matchConfig, ISessionConfig sessionConfig, IEnumerable <IThinkerPrototype> thinkerPrototypes, IEnumerable <IThinkerListener> thinkerListeners, IEnumerable <IMatchListener> matchListeners, IEnumerable <ISessionListener> sessionListeners) { // Keep parameters this.matchConfig = matchConfig; this.sessionConfig = sessionConfig; this.thinkerPrototypes = thinkerPrototypes; this.thinkerListeners = thinkerListeners; this.matchListeners = matchListeners; // Register session listeners foreach (ISessionListener listener in sessionListeners) { listener.ListenTo(this); } // Instantiate list of current thinkers currentThinkers = new IThinker[2]; }
// Awake is called when the script instance is being loaded private void Awake() { // Top and bottom pole padding const float polePadding = 0.1f; // Ground instance GameObject groundInst, messageBox; // Top-left of ground sprite renderer Vector3 gTopLeft; // Bounds of different sprite renderers Bounds gBounds, plBounds, aBounds, pcBounds; // Get reference to the camera Camera camera = GameObject.Find("Main Camera").GetComponent <Camera>(); // Get reference to the match view configuration IMatchViewConfig viewConfig = GetComponentInParent <IMatchViewConfig>(); // Get value of last move animation length in seconds lastMoveAnimLength = viewConfig.LastMoveAnimLength; // /////////////////////////////////// // // Get references to essential prefabs // // /////////////////////////////////// // whiteRoundPiece = Resources.Load <GameObject>("Prefabs/PieceWhiteRound"); whiteSquarePiece = Resources.Load <GameObject>("Prefabs/PieceWhiteSquare"); redRoundPiece = Resources.Load <GameObject>("Prefabs/PieceRedRound"); redSquarePiece = Resources.Load <GameObject>("Prefabs/PieceRedSquare"); pole = Resources.Load <GameObject>("Prefabs/Pole"); ground = Resources.Load <GameObject>("Prefabs/Ground"); arrowButton = Resources.Load <GameObject>("Prefabs/ArrowButton"); playerPanel = Resources.Load <GameObject>("Prefabs/PlayerPanel"); // //////////////////////////////////////////// // // Initialize required variables and coroutines // // //////////////////////////////////////////// // // Instantiate a string builder, used for keeping messages messages = new StringBuilder(); // We just started, so game is not finished yet finished = false; // Get reference to the match configuration matchConfig = GetComponentInParent <IMatchConfig>(); // Get reference to the session data and the game board matchData = GetComponentInParent <IMatchDataProvider>(); board = matchData.Board; // Both players have the round shapes initially selected by default selectedShapes = new PShape[] { PShape.Round, PShape.Round }; // Instantiate Unity events for shape selection and board updating ShapeSelected = new ColorShapeEvent(); BoardUpdated = new UnityEvent(); // Create matrix for placing game objects representing pieces pieces = new GameObject[board.rows, board.cols]; // Create array for UI arrow script objects uiArrows = new HumanMoveButton[board.cols]; // Instantiate the message queue messageQueue = new Queue <string>(); // Get reference to the "Message Box" canvas game object, set the // reference to the main camera, and get a reference to the UI text // to display the messages messageBox = GameObject.Find("MessageBox").gameObject; messageBox.GetComponent <Canvas>().worldCamera = camera; messageBoxText = messageBox.GetComponentInChildren <Text>(); // Initialize message box coroutine StartCoroutine(UpdateMessageBox()); // /////////////////////////////////////// // // Initialize and place game board objects // // /////////////////////////////////////// // // Instantiate ground groundInst = Instantiate(ground, transform); // Determine where ground starts, since everything will be placed // with respect to the ground gBounds = groundInst.GetComponent <SpriteRenderer>().bounds; gTopLeft = new Vector3(gBounds.min.x, gBounds.max.y, 0); // Get pole bounds plBounds = pole.GetComponent <SpriteRenderer>().bounds; // Get arrow bounds aBounds = arrowButton.GetComponent <SpriteRenderer>().bounds; // Get piece bounds (any will do) pcBounds = redRoundPiece.GetComponent <SpriteRenderer>().bounds; // Instantiate poles and arrows for (int c = 0; c < board.cols; c++) { GameObject currPole, currArrow; // Instantiate current pole currPole = Instantiate( pole, new Vector3( gTopLeft.x + (c + 1) * (gBounds.size.x / (board.cols + 1)), gTopLeft.y + plBounds.extents.y - polePadding, 1), Quaternion.identity, transform); currPole.name = $"Pole{c}"; // Instantiate current arrow currArrow = Instantiate( arrowButton, new Vector3( gTopLeft.x + (c + 1) * (gBounds.size.x / (board.cols + 1)), gTopLeft.y + plBounds.size.y + polePadding + aBounds.extents.y, 4), Quaternion.identity, transform); currArrow.name = $"Arrow{c}"; // Keep reference to the UI arrow script uiArrows[c] = currArrow.GetComponent <HumanMoveButton>(); // Set the arrow's column uiArrows[c].Column = c; // Listen to arrow clicks in order to perform move selection uiArrows[c].Click.AddListener(OnMoveSelected); // Enable or disable arrow depending on who's playing currArrow.SetActive(matchData.CurrentThinker is HumanThinker); } // These will be necessary for calculating the positions of the // pieces leftPoleBase = new Vector2( gTopLeft.x + gBounds.size.x / (board.cols + 1), gTopLeft.y); distBtwPoles = gBounds.size.x / (board.cols + 1); totalHeightForPieces = plBounds.size.y - 2 * polePadding; // The scale of the pieces will the minimum between... piecesScale = Mathf.Min( // ...half of distance between the poles divided by the original // width of the prefabs... (distBtwPoles / 2) / pcBounds.size.x, // ...and the available space for each piece in a pole, divided // by the original height of the prefabs (totalHeightForPieces / board.rows) / pcBounds.size.y); // Keep the length of the pieces (equal in x and y directions) piecesLength = piecesScale * pcBounds.size.y; // /////////////////// // // Setup player panels // // /////////////////// // // Instantiate player panels GameObject[] playerPanels = { Instantiate(playerPanel, transform), Instantiate(playerPanel, transform) }; // Initialize an array of piece sprites, which will simplify // passing the correct sprite for each shape in each player panel Sprite[,] pieceSprites = { { whiteRoundPiece.GetComponent <SpriteRenderer>().sprite, whiteSquarePiece.GetComponent <SpriteRenderer>().sprite, }, { redRoundPiece.GetComponent <SpriteRenderer>().sprite, redSquarePiece.GetComponent <SpriteRenderer>().sprite, } }; // Initialize panels for each player for (int i = 0; i < 2; i++) { // Current player PColor player = (PColor)i; // Get current panel GameObject panel = playerPanels[i]; // Get current panel's rect transform RectTransform rtPanel = panel.GetComponent <RectTransform>(); // Get current panel toggles for selecting shape Toggle[] toggles = panel.GetComponentsInChildren <Toggle>(); // Setup event camera in panel panel.GetComponent <Canvas>().worldCamera = camera; // Position panel rtPanel.position = new Vector3( i == 0 // First panel to the right ? gBounds.center.x - gBounds.extents.x / 2 // Second panel to the left : gBounds.center.x + gBounds.extents.x / 2, gBounds.min.y - rtPanel.rect.height / 2 * rtPanel.localScale.y, rtPanel.position.z ); // Set player name in panel panel.GetComponentInChildren <Text>().text = matchData.GetThinker(player).ToString(); // Configure toggles for selecting shape for (int j = 0; j < 2; j++) { // Current shape PShape shape = (PShape)j; // Setup correct sprite for the toggle toggles[j] .transform .GetChild(1) .GetComponent <Image>() .sprite = pieceSprites[i, j]; // Delegate to be called at start and after each move, // which: // 1. Updates piece count in current player's toggle UI // widget // 2. Enables/disables toggle interaction depending on // who's playing UnityAction setToggles = () => { // 1. // Count for current player and shape int count = board.PieceCount(player, shape); // Update label to update with shape count toggles[(int)shape].GetComponentInChildren <Text>() .text = count.ToString(); // If count reached zero, swap shape selection if (count == 0) { SelectShape( player, shape == PShape.Round ? PShape.Square : PShape.Round); toggles[(int)shape].interactable = false; } // 2. else { // Player is human, is its turn and game not over, // enable toggle if (matchData.GetThinker(player) is HumanThinker && player == board.Turn && !finished) { toggles[(int)shape].interactable = true; } // Otherwise disable toggle else { toggles[(int)shape].interactable = false; } } }; // Invoke delegate to initialize toggles setToggles.Invoke(); // Make this delegate be called after each move BoardUpdated.AddListener(setToggles); // Wire up method for listening to piece swap events toggles[j].onValueChanged.AddListener( b => { if (b) { SelectShape(player, shape); } }); } // Wire up listener for programatically changing selected shape // in current player's toggle ShapeSelected.AddListener((PColor p, PShape s) => { if (p == player) { toggles[(int)s].isOn = true; } }); } }