Ejemplo n.º 1
0
        // 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));
                }
            }
        }
Ejemplo n.º 4
0
        // //////////////////////////////// //
        // 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;
                                            }
                                          });
            }
        }