Beispiel #1
0
        private void uxDisplayResponse(PuzzleResponse pr)
        {
            Color c = this.color_IncorrectAnswer;

            if (pr.type == PuzzleResponse.ResponseType.Correct)
            {
                c = this.color_CorrectAnswer;
            }
            else if (pr.type == PuzzleResponse.ResponseType.AskLater)
            {
                c = this.color_DelayedAnswer;
            }
            this.responseRichTextBox.ForeColor = c;
            this.responseRichTextBox.Text      = pr.response;
            this.oracleButton.Hide();
            this.responsePanel.Show();
        }
Beispiel #2
0
        private void button1_Click(object sender, EventArgs e)
        {
            Debug.WriteLine("Verify button clicked!");
            uxResetIdleTimer();
            String id     = this.idTextBox.Text;
            String answer = this.answerTextBox.Text;

            // Let's ask the oracle!
            if (id.Length > 0)
            {
                PuzzleResponse pr = oracle.checkSolution(id, answer);
                try
                {
                    oracleLogger.logSolveAttempt(id, answer, pr);
                    uxDisplayResponse(pr);
                }
                catch (ApplicationException ex)
                {
                    handleFatalError(ex);
                }
            }
        }
        public void logSolveAttempt(String puzzleId, String attemptedSolution, PuzzleResponse response)
        {
            // We log the normalized attempt so that it doesn't have extraneous characters.
            attemptedSolution = PuzzleOracle.normalizeSolution(attemptedSolution);

            String responseCode = "INVALID";

            switch (response.type)
            {
            case PuzzleResponse.ResponseType.AskLater:
                responseCode = "BLACKLISTED";
                break;

            case PuzzleResponse.ResponseType.Correct:
                responseCode = "CORRECT";
                break;

            case PuzzleResponse.ResponseType.Incorrect:
                responseCode = "INCORRECT";     // INCORRET means it matched a hint.
                break;

            case PuzzleResponse.ResponseType.NotFound:
                responseCode = "NOTFOUND";
                break;

            default:
                responseCode = "UNRECOGNIZED_CODE";
                break;
            }

            // Encrypt...
            String customizer = teamId + puzzleId; // Important - this is the customizer format used for encryption.

            responseCode      = CryptoHelper.simpleEncryptDecrypt(LOG_PASSWORD, customizer, LOG_ENCRYPT_CHARS, responseCode, true);
            attemptedSolution = CryptoHelper.simpleEncryptDecrypt(LOG_PASSWORD, customizer, LOG_ENCRYPT_CHARS, attemptedSolution, true);

            rawLog(puzzleId, responseCode, attemptedSolution);
        }
Beispiel #4
0
        /// <summary>
        /// Adds all puzzles in the specified spreadsheet. Individual puzzles may not
        /// be added for a variety of reasons including:
        ///     - Duplicate Puzzle ID (only first one gets added)
        ///     - Empty answer field.
        ///     - Invalid REGEX field in answer or hints.
        /// Errors are reported as Trace output.
        /// TODO: Have a systematic way to report skipped puzzles.
        /// </summary>
        /// <param name="sr"></param>
        private void addPuzzles(SimpleSpreadsheetReader sr)
        {
            // We expect that the first row contains the signature ("POD") followed by "version:1.0" followed by
            // additional properties (which we ignore for now)
            const int    HEADER_ROWS    = 2;
            const int    MIN_COLS       = 3;
            const String FILE_SIGNATURE = "POD";
            const String PROP_ENCRYPTED = "encrypted";
            int          sheet          = 0;
            bool         encrypted      = false; // whether answers and hints are encrypted or not.
            int          numRows        = sr.getNumRows(sheet);
            int          numCols        = sr.getNumCols(sheet);

            if (numRows < HEADER_ROWS || numCols < MIN_COLS)
            {
                ErrorReport.logError("Puzzle data spreadsheet is too small!");
                throw new ArgumentException();
            }
            String[] propertyRow = sr.getRowCells(0, 0, numCols - 1, sheet);
            String[] header      = sr.getRowCells(1, 0, numCols - 1, sheet);

            // We expect the first property cell to be POD (all caps)
            if (!Utils.stripEndBlanks(propertyRow[0]).Equals(FILE_SIGNATURE))
            {
                ErrorReport.logError("Puzzle data spreadsheet has an invalid/missing signature.");
                throw new ArgumentException();
            }

            // Read rest of properties
            readProperties(propertyRow);

            // Check if answer keys are encrypted.
            if (properties.ContainsKey(PROP_ENCRYPTED))
            {
                encrypted = true;
            }
            this.sourceIsEncrypted = encrypted;

            // We expect the first header cell to be "id"
            String headerId = Utils.stripEndBlanks(header[0]);

            if (!headerId.Equals(SPREADSHEET_LABEL_ID, StringComparison.CurrentCultureIgnoreCase))
            {
                ErrorReport.logError("Puzzle data spreadsheet does not have the ID field.");
                throw new ArgumentException();
            }
            int startRow = HEADER_ROWS; // First row of puzzle data
            int startCol = 0;           // First col of puzzle data

            //int puzzleCount = numRows - HEADER_ROWS; // could be zero; it's valid to have 0 puzzles.
            for (int i = startRow; i < numRows; i++)
            {
                String[]     sRow     = sr.getRowCells(i, startCol, numCols - 1, sheet);
                const String REGEX_ID = @"^[0-9][0-9][0-9]$"; // For now, IDs must be 3-digit numbers.
                String       id       = Utils.stripBlanks(sRow[0]);
                if (!Regex.IsMatch(id, REGEX_ID))
                {
                    Trace.WriteLine(String.Format("Skipping row {0}: invalid ID", i));
                    continue;
                }

                // We got the ID, if needed decrypt remaining fields after Name
                if (encrypted)
                {
                    decryptCells(id, sRow, 2, numCols - 1); // 2 == skip Id and Name. False == descrypt
                }

                //  Now let's get the remaining columns. First,  get the first two: Name and Answer.

                String name   = Utils.stripEndBlanks(sRow[1]);
                String answer = Utils.stripEndBlanks(sRow[2]);
                // Neither should be blank.
                if (name.Equals("") || answer.Equals(""))
                {
                    Trace.WriteLine(String.Format("Skipping row {0}: blank Name or Answer", i));
                    continue;
                }

                PuzzleResponse pAnswerResponse = buildPuzzleResponse(answer, PuzzleResponse.ResponseType.Correct);
                if (pAnswerResponse == null)
                {
                    Trace.WriteLine(String.Format("Skipping row {0}: Invalid Answer", i));
                    continue;
                }
                PuzzleInfo pi = new PuzzleInfo(id, name);
                pi.addResponse(pAnswerResponse);

                // Add hints, if any...
                for (int j = 3; j < sRow.Length; j++)
                {
                    String field = Utils.stripEndBlanks(sRow[j]);
                    if (field.Length > 0)
                    {
                        PuzzleResponse pr = buildPuzzleResponse(field, PuzzleResponse.ResponseType.Incorrect);
                        if (pr == null)
                        {
                            Trace.WriteLine(String.Format("Ignoring hint {0} on row {1}: Invalid hint content", j, i));
                            continue;
                        }
                        pi.addResponse(pr);
                    }
                }

                try
                {
                    puzzles.Add(id, pi);
                    puzzleIDs.Add(id);
                    Trace.WriteLine(String.Format("Adding row {0}: Puzzle ID {1}, Answer {2}", i, id, answer));
                }
                catch (ArgumentException)
                {
                    Trace.WriteLine(String.Format("Ignoring row {0}: Duplicate ID {1}", i, id));
                }
            }
        }
Beispiel #5
0
        /// <summary>
        /// Constructs a response for the user-generated solution. puzzleId
        /// MUST reference a valid puzzle else a KeyNotFound exception is
        /// thrown (call tryGetName first if not sure).
        /// </summary>
        /// <param name="puzzleId"></param>
        /// <param name="solution"></param>
        /// <returns></returns>
        public PuzzleResponse checkSolution(string puzzleId, string solution)
        {
            // Lookup puzzle...
            solution = normalizeSolution(solution);
            PuzzleInfo     pi = puzzles[puzzleId];
            PuzzleResponse pr = null;

            // Check blacklist state (<0 means we're ok)
            int delay = pi.blacklister.submitDelay;

            // Have we solved this before?
            Boolean alreadySolved = pi.puzzleSolved;


            // If we are not already solved, and we are blacklisted, we return a special "try later" message.
            if (!alreadySolved && delay > 0)
            {
                String sResponse;

                // Blacklisted!
                Boolean permanentlyBlacklisted = delay == Blacklister.BLACKLIST_FOREVER_TIME;
                if (permanentlyBlacklisted)
                {
                    sResponse = PERMANENT_BLACKLISTED_RESPONSE;
                }
                else
                {
                    String sDelay = delay + " seconds";
                    if (delay > 60)
                    {
                        int minutes = delay / 60;
                        int seconds = delay % 60;
                        sDelay = minutes + " minute" + ((minutes == 1) ? "" : "s");
                        if (seconds > 0)
                        {
                            sDelay += " and " + seconds + " seconds";
                        }
                    }
                    sResponse = String.Format(BLACKLISTED_RESPONSE, sDelay, pi.puzzleId);
                }
                pr = new PuzzleResponse(solution, PuzzleResponse.ResponseType.AskLater, sResponse);
                return(pr); // ***************** EARLY RETURN *******************
            }

            pr = pi.matchResponse(solution);
            if (pr == null)
            {
                pr = new PuzzleResponse(solution, PuzzleResponse.ResponseType.NotFound, GENERIC_INCORRECT_RESPONSE);
            }

            pi.blacklister.registerSubmission();

            // If already solved, but solution is not correct, we put a special message.
            if (!alreadySolved)
            {
                pi.puzzleSolved = (pr.type == PuzzleResponse.ResponseType.Correct);
            }
            else if (pr.type != PuzzleResponse.ResponseType.Correct)
            {
                // Puzzle has been solved before but there is a new, incorrect submission. We give a helpful message to the user.
                pr = new PuzzleResponse(solution, pr.type, INCORRECT_BUT_PUZZLE_ANSWERED_BEFORE);
            }

            return(pr);
        }
Beispiel #6
0
 public void addResponse(PuzzleResponse pr)
 {
     responses.Add(pr);
 }