Untitled

mail@pastecode.io avatar
unknown
matlab
2 years ago
24 kB
2
Indexable
Never
% EXPLANATION OF SPRITE SHEET (othelloIcons.png)
% The majority of the sprite sheet corresponds to ASCII shifted over 1 for
% ease of use with Matlab. Since Matlab treats 'abc' as an array like
% [97 98 99], the ASCII codes for the respective letters, the sprite sheet
% was made such that the indexes 97, 98, and 99 correspond to a, b, and c.
% This is true for all printable characters.
% OTHER SPRITE VALUES
% 1: Empty green tile 2: Black piece with border 3: White piece with border
% 4: Empty tile       5: Black piece no border   6: White piece no border
% 7: All black        8: Light green (selection) 9: All yellow
% 10: Chinese symbol  11: Switch icon

clc
clear
close all

% create board from personal icon set
symbols = simpleGameEngine("othelloIcons.png",16,16,4,[0,140,0]);

% get language from user ( 1 => English, 2 => Spanish )
board = zeros(10,19) + 7;
board(4:7,2:18) = 4;
board(2,7:13) = 'REVERSI';
board(4,9:11) = ['A' 11 10]; % internationally understood translate icons
board(5,2:8) = 'English';
board(5,12:18) = 'Español';
language = 0;
drawScene(symbols,board);
select = zeros(10,19) + 4;
while true
    drawScene(symbols,select,board);
    % if player selected a language, wait for a second then switch screens
    if language ~= 0
        pause(.05);
        if language == 3
            pause(.3)
        end
        break;
    end
    % select is empty aside from yellow boxes to go under selections
    select = zeros(10,19) + 4;
    [row, col] = getMouseInput(symbols);
    if row == 5
        if col > 1 && col < 9
            language = 1;
            select(5,2:8) = 9;
        elseif col > 11 && col < 19
            language = 2;
            select(5,12:18) = 9;
        end
    elseif row == 6
        if col > 1 && col < 14
            language = 3;
            % name of this language only appears if clicked as easter egg
            board(6,2:13) = 'tlhIngan Hol';
            select(6,2:13) = 9;
        end
    end
end

% build main screen board
if language == 1
    board(4,2:18) = 'PLAYER1   PLAYER2';
    board(5,2:18) = ' Human     Human ';
    board(6,2:18) = ' AI123     AI123 ';
    board(7,2:18) = '      Start      ';
    board(9,5:15) = 'How to play';
elseif language == 2
    board(4,2:18) = 'JUGADOR1 JUGADOR2';
    board(5,2:18) = ' Humano   Humano ';
    board(6,2:18) = ' IA 123   IA 123 ';
    board(7,2:18) = '     Empezar     ';
    board(9,4:16) = 'Cómo se Juega';
elseif language == 3
    board(4,2:18) = 'QujwI''1   QujwI''2';
    board(5,2:18) = ' Human     Human ';
    board(6,2:18) = 'De'' 123   De'' 123';
    board(7,2:18) = '      tagh!      ';
end

% default variables, the difficulty is automatically stored as 2 in case a
% user just clicks 'AI'
start = false;
p1isAI = false;
p2isAI = false;
difficulty1 = 2;
difficulty2 = 2;
% MAIN MENU SCREEN
while true
    % select is empty aside from yellow boxes to go under selections
    select = zeros(10,19) + 4;
    % puts yellow under selections for Player 1
    if p1isAI
        if language == 1
            select(6,3:4) = 9;
            select(6,4+difficulty1) = 9;
        elseif language == 2
            select(6,3:4) = 9;
            select(6,5+difficulty1) = 9;
        elseif language == 3
            select(6,2:4) = 9;
            select(6,5+difficulty1) = 9;
        end
    else
        if language == 1
            select(5,3:7) = 9;
        elseif language == 2
            select(5,3:8) = 9;
        elseif language == 3
            select(5,3:7) = 9;
        end
    end
    % puts yellow under selections for Player 2
    if p2isAI
        if language == 1
            select(6,13:14) = 9;
            select(6,14+difficulty2) = 9;
        elseif language == 2
            select(6,12:13) = 9;
            select(6,14+difficulty2) = 9;
        elseif language == 3
            select(6,12:14) = 9;
            select(6,15+difficulty2) = 9;
        end
    else
        if language == 1
            select(5,13:17) = 9;
        elseif language == 2
            select(5,12:17) = 9;
        elseif language == 3
            select(5,13:17) = 9;
        end
    end
    % draws the menu
    drawScene(symbols,select,board);
    % if player selected start, wait for a second then switch screens
    [row, col] = getMouseInput(symbols);
    % if player selects start
    if (language == 1 && row == 7 && col > 7 && col < 13) || ...
       (language == 2 && row == 7 && col > 6 && col < 14) || ...
       (language == 3 && row == 7 && col > 7 && col < 13)
        % put yellow under start
        if language == 1
            select(7,8:12) = 9;
        elseif language == 2
            select(7,7:13) = 9;
        elseif language == 3
            select(7,8:12) = 9;
        end
        drawScene(symbols,select,board);
        pause(.05);
        % exits the loop
        break;
    % if player selects human
    elseif row == 5
        if (language == 1 && col > 2 && col < 8) || ...
           (language == 2 && col > 2 && col < 9) || ...
           (language == 3 && col > 2 && col < 8)
            p1isAI = false;
        elseif (language == 1 && col > 12 && col < 18) || ...
               (language == 2 && col > 11 && col < 18) || ...
               (language == 3 && col > 12 && col < 18) 
            p2isAI = false;
        end
    % if player selects AI, assign difficulty
    elseif row == 6
        if (language == 1 && col > 2 && col < 8) || ...
           (language == 2 && col > 2 && col < 9) || ...
           (language == 3 && col > 1 && col < 9)
            p1isAI = true;
            if language == 1 && col > 4
                % '- 4' is the spacing between the column and the actual
                % number (in English)
                difficulty1 = col - 4;
            elseif language == 2 && col > 5
                difficulty1 = col - 5;
            elseif language == 3 && col > 5
                difficulty1 = col - 5;
            end
        elseif (language == 1 && col > 12 && col < 18) || ...
               (language == 2 && col > 11 && col < 18) || ...
               (language == 3 && col > 10 && col < 19)
            p2isAI = true;
            if language == 1 && col > 14
                difficulty2 = col - 14;
            elseif language == 2 && col > 14
                difficulty2 = col - 14;
            elseif language == 3 && col > 15
                difficulty2 = col - 15;
            end
        end
    % if player selects how to play, show tutorial
    elseif (language == 1 && row == 9 && col > 4 && col < 16) || ...
           (language == 2 && row == 9 && col > 3 && col < 17)
        % show selection for .05 seconds
        if language == 1
            select(9,5:15) = 9;
        elseif language == 2
            select(9,4:16) = 9;
        end
        drawScene(symbols,select,board);
        pause(.05);
        showTutorial(symbols, language);
    end
end

% reset board
board = newBoard(language);
% start game on black's turn (black always goes first)
blacksTurn = true;
% keep track of who can move, stop game if neither black/white can
whiteCanMove = true;
blackCanMove = true;

% MAIN GAME LOOP
quit = false;
while ~quit

    % for whoever's turn it is, check if they can move while looking
    % through board
    if blacksTurn
        blackCanMove = false;
    else
        whiteCanMove = false;
    end
    % iterate through all spaces on the board (board(2:9,2:9))
    for boardRow = 2:9
        for boardCol = 2:9
            % gets rid of highlight spaces
            if board(boardRow, boardCol) == 8
                board(boardRow, boardCol) = 1;
            end
            % decide whether or not a player can move in a given space
            if board(boardRow, boardCol) == 1
                % iterate through directions from current space
                for scopeRow = -1:1
                    for scopeCol = -1:1
                        % move testRow/testCol in current direction
                        testRow = boardRow + scopeRow;
                        testCol = boardCol + scopeCol;
                        % while the pieces in this direction are
                        % opponent's color
                        while board(testRow, testCol) == 2 + blacksTurn
                            testRow = testRow + scopeRow;
                            testCol = testCol + scopeCol;
                            % if new test space is player's color, then 
                            % this is playable space (and player is
                            % able to play)
                            if board(testRow, testCol) == 3 - blacksTurn
                                board(boardRow, boardCol) = 8;
                                if blacksTurn
                                    blackCanMove = true;
                                else
                                    whiteCanMove = true;
                                end
                            end
                        end
                    end
                end
            end
        end
    end
    % if player can move, then they will. Otherwise either turn is
    % skipped or if nobody can play game ends
    if (blacksTurn && blackCanMove) || (~blacksTurn && whiteCanMove)
        % this puts the current score at the bottom
        board = setCounts(board);
        % label current turn and draw game
        board(4, 17) = 6 - blacksTurn;
        drawScene(symbols, board);
        % title can have comments from last move, so remove it
        title("");
        % if a human has a turn
        if (blacksTurn && ~p1isAI) || (~blacksTurn && ~p2isAI)
            % get a location to play from the player
            row = 1;
            col = 1;
            % until player clicks a highlighted space
            while board(row,col) ~= 8
                [row, col] = getMouseInput(symbols);
                if board(row,col) ~= 8
                    % if the player clicks redo
                    if (row == 7 && (col > 10 && col < 19))
                        % show selection
                        select = zeros(10,19) + 4;
                        select(7,11:18) = 9;
                        drawScene(symbols,select,board);
                        pause(.05);
                        % reset board
                        board = newBoard(language);
                        % start game on black's turn (black always goes first)
                        blacksTurn = true;
                        % keep track of who can move, stop game if neither black/white can
                        whiteCanMove = true;
                        blackCanMove = true;
                        break;
                    elseif (row == 9  && (col > 10 && col < 19))
                        % show selection
                        select = zeros(10,19) + 4;
                        select(9,11:18) = 9;
                        drawScene(symbols,select,board);
                        pause(.05);
                        % quit game
                        quit = true;
                        break;
                    else
                        if language == 1
                            title("Error: Invalid Space");
                        elseif language == 2
                            title("Error: Espacio Inválido");
                        end
                    end
                end
            end
        % if AI has a turn
        else
            % delay between AI moves
            pause(.2);
            % sets difficulty to that of either black or white,
            % depending on which color current AI is playing
            difficulty = blacksTurn * difficulty1 + ~blacksTurn * difficulty2;
            [row, col] = getAIMove(board, blacksTurn, difficulty);
        end
        % place piece on the board and switch turn to other player if move
        % is valid (otherwise the game was probably reset)
        if (row > 1 && row < 10) && (col > 1 && col < 10)
            board = updateBoard(board, row, col, blacksTurn);
            blacksTurn = ~blacksTurn;
        end
    elseif ~blackCanMove && ~whiteCanMove
        % stop game if neither one can play until input

        % replace the turn label with "GAMEOVER"
        if language == 1
            board(4,11:18) = 'GAMEOVER';
        elseif language == 2
            board(4,11:18) = ' EL FIN ';
        end
        % set final score
        board = setCounts(board);
        drawScene(symbols,board);
        title("");

        row = 1;
        col = 1;
        % await input
        while true
            [row, col] = getMouseInput(symbols);
            % redo button is clicked
            if (row == 7 && (col > 10 && col < 19))
                % show selection
                select = zeros(10,19) + 4;
                select(7,11:18) = 9;
                drawScene(symbols,select,board);
                pause(.05);
                % reset board
                board = newBoard(language);
                % start game on black's turn (black always goes first)
                blacksTurn = true;
                % keep track of who can move, stop game if neither black/white can
                whiteCanMove = true;
                blackCanMove = true;
                break;
            % quit button clicked
            elseif (row == 9  && (col > 10 && col < 19))
                % show selection
                select = zeros(10,19) + 4;
                select(9,11:18) = 9;
                drawScene(symbols,select,board);
                pause(.05);
                % exit game
                quit = true;
                break;
            end
        end
    else 
        % skip turn if one user can't play
        blacksTurn = ~blacksTurn;
    end
end
close;

function board = newBoard(language)
    % set up starting board to all black
    board = zeros(10,19) + 7;
    % set game area to green grid
    board(2:9,2:9) = 1;
    % set up message area (turn, score, control)
    board(2:9,11:18) = 4;
    board(5:6,11:18) = 7;
    board(8,11:18) = 7;
    if language == 1
        board(2,12:17) = 'Score:';
        board(4,12:16) = 'Turn:';
        board(7,13:16) = 'Redo';
        board(9,13:16) = 'Quit';
    elseif language == 2
        board(2,11:18) = 'Puntaje:';
        board(4,11:16) = 'Turno:';
        board(7,12:17) = 'Replay';
        board(9,12:17) = 'Cerrar';
    elseif language == 3
        board(2,11:18) = 'pe''''egh:';
        board(4,11:16) = 'yiQuj:';
        board(7,13:16) = 'tagh';
        board(9,13:15) = 'bup';
    end
    board(3,11) = 5;
    board(3,18) = 6;
    % place starting pieces
    board(5,5) = 2;
    board(6,6) = 2;
    board(5,6) = 3;
    board(6,5) = 3;

end
function board = setCounts(board)   
    % update score on board

    blackCt = getCount(board, 2);
    whiteCt = getCount(board, 3);
    board(3,13) = 4;
    board(3, 12:12+(blackCt>9)) = num2str(blackCt);
    board(3,16) = 4;
    board(3, 17-(whiteCt>9):17 ) = num2str(whiteCt);
end

function count = getCount(board, value)
    % return the number of value on the playable board

    count = 0;
    % get number of pieces on board with value
    for boardRow = 2:9
        for boardCol = 2:9
            if board(boardRow, boardCol) == value
                count = count + 1;
            end
        end
    end
end
function showTutorial(symbols, language)
    if language == 1
        buildBook("RULES: Black always starts. For every move, " + ...
        "every line between the piece you play and another piece of " + ...
        "your color separated only by pieces of the opponent's " + ...
        "color gets flipped to your color. You may only play in " + ...
        "places where you are able to flip pieces, conveniently " + ...
        "highlighted in this application. If one player is unable " + ...
        "to play on their turn, the other player gets an extra " + ...
        "turn. The game ends when no players are able to play, and " + ...
        "the winner is determined by the player with the most " + ...
        "pieces of their color. STRATEGY: Take control of the " + ...
        "corners. In most games in which one player has 3 of the " + ...
        "corners, that player wins. Similarly, it is important to " + ...
        "take control of the edges. GAME INSTRUCTIONS: For each of " + ...
        "player 1 and player 2 (black and white respectively), " + ...
        "select human for if they are to be played by a human or AI " + ...
        "with the corresponding difficulty level if they are to be " + ...
        "played by an AI. Click start to begin the game.",symbols);
    elseif language == 2
        buildBook("REGLAS: Negro siempre empieza. Para cada turno, " + ...
        "cada línea seperada solamente por piezas del color del " + ...
        "adversario se voltea a tu color. Solamente puedes poner " + ...
        "una pieza en un lugar en cual puedes voltear piezas, " + ...
        "destacados en esta aplicación. Si un jugador no se puede " + ...
        "jugar, el otro jugador gana extra turno. El juego termina " + ...
        "cuando nadie puede jugar, y se sabe el ganador por quién " + ...
        "tenga la mayoría de las piezas. ESTRATEGÍA: Toma control " + ...
        "de las esquinas, y también de los bordes.",symbols);
    end
end
function buildBook(text, symbols)
    % prints all of text with precondition that no word in text is longer
    % than 16 characters in a "book" in which one can search through the
    % pages.

    % get array of all words in text
    words = split(text);
    line = 2;
    col = 2;
    pageCt = 1;
    % standard blank green page with black border
    newPage = zeros(10,19) + 7;
    newPage(2:9,2:18) = 4;
    newPage(9,2) = '<';
    newPage(9,18) = '>';
    newPage(9,10) = '×';
    % initialize board with single page
    board = newPage;
    % gets rid of first '<'
    board(9,2) = 4;
    while ~isempty(words)
        % get char array of first word
        word = convertStringsToChars(words(1));
        % remove first word
        words = words(2:end);
        % if word can't fit on current line
        if col + length(word) > 19
            col = 2;
            % add a page if lines run out, otherwise go to next line
            if line == 8
                board = [board, newPage];
                line = 2;
                pageCt = pageCt + 1;
            else
                line = line + 1;
            end
        end
        % add word to board
        board(line,(pageCt-1)*19+col:(pageCt-1)*19+col+length(word)-1) = word;
        % increase col accordingly
        col = col + length(word) + 1;
    end
    % remove final increase page button
    board(9,18 + (pageCt-1)*19) = 4;
    % display book
    page = 1;
    % runs the book until user clicks 'end'
    while true
        drawScene(symbols,board(1:end,19*(page-1)+1:19*page));
        [row, col] = getMouseInput(symbols);
        if row == 9
            % clicked end
            if col == 10
                break;
            % clicking back arrow    
            elseif col == 2 && page > 1
                page = page - 1;
            %clicking front arrow
            elseif col == 18 && page < pageCt
                page = page + 1;
            end
        end
    end
end

function board = updateBoard(board, row, col, blacksTurn)
    % place a piece at the indicated space. Must be a playable space.
    for scopeRow = -1:1
        for scopeCol = -1:1
            % test if line can flip in this direction
            testRow = row + scopeRow;
            testCol = col + scopeCol;
            canFlipLine = false;
            while board(testRow, testCol) == 2 + blacksTurn
                testRow = testRow + scopeRow;
                testCol = testCol + scopeCol;
                if board(testRow, testCol) == 3 - blacksTurn
                    canFlipLine = true;
                end
            end
            % flip line if it can in this direction
            if canFlipLine 
                while testRow ~= row + scopeRow || testCol ~= col + scopeCol
                    testRow = testRow - scopeRow;
                    testCol = testCol - scopeCol;
                    board(testRow, testCol) = 3 - blacksTurn;
                end
            end
        end
    end
    board(row,col) = 3 - blacksTurn;
end

function [row, col] = getAIMove(board, blacksTurn, difficulty)
    % returns the row and column that the AI chooses.
    [rows, cols, scores] = getScores(board, blacksTurn);
    scores = difficulty .^ (scores + 1);

    % select a random number up to the the sum of the scores and select the
    % choose the row and column associated with where that random number
    % falls on the scores array.
    sumScores = rand*sum(scores,'all');
    count = 0;
    for i = 1:length(scores)
        count = count + scores(i);
        if count > sumScores
            row = rows(i);
            col = cols(i);
            break;
        end
    end
end
function [rows, cols, scores] = getScores(board, blacksTurn) 
    % for each space on the board with an 8, the row and col for that space
    % is added to the rows and cols arrays and the score (calculated from
    % consideration of several variables) is 
    % added to the scores array.
    
    % length is how many highlighted spaces on the board
    length = getCount(board, 8);
    rows = zeros(1,length);
    cols = zeros(1,length);
    scores = zeros(1,length);
    i = 1;
    for row = 2:9
        for col = 2:9
            if board(row, col) == 8
                rows(i) = row;
                cols(i) = col;
                % tests what would happen if player puts piece here
                testBoard = updateBoard(board, row, col, blacksTurn);
                opportunities = 0;
                % sum of space types of all current player's flipped pieces
                plScore = 0;
                for testRow = 2:9
                    for testCol = 2:9
                        if testBoard(testRow, testCol) == 8
                            opportunities = opportunities + getSpaceType(testRow, testCol);   
                        elseif testBoard(testRow, testCol) == 3 - blacksTurn
                            plScore = plScore + getSpaceType(testRow,testCol);
                        end
                    end
                end
                % calculate score
                scores(i) = getSpaceType(row, col)*10 + plScore - opportunities/2 + 100*(getCount(testBoard, 8) == 0);
                % should rarely ever be this low, but possible. Regardless,
                % must be positive.
                if scores(i) < .1
                    scores(i) = .1;
                end
                
                i = i + 1;
            end
        end
    end

end

function spaceType = getSpaceType(row, col)
    % return spaceType as a number [0,5] as labeled on the strategy sheet
    % based on the type of space is at the given row and column
    
    if (row == 5 || row == 6) && (col == 5 || col == 6)
        spaceType = 4;
    % Inner Square around center
    elseif((row == 4 || row == 7) && (col == 4 || col == 7))
        spaceType = 3;
    % Square outside of the inner square, making the corners of this square
    % 0
    elseif((row >=3 && row <= 8) && (col >= 3 && col <= 8))
        spaceType = 2;
        % Make corners 0
        if((row == 3 || row == 8) && (col == 3 || col == 8))
            spaceType = 0;
        end
    % The rest of the spaces are defaulted to 4 besides the corners and the
    % adjacent spaces
    else
        spaceType = 4;
        % Make corners 5
        if((row == 2 || row == 9) && (col == 2 || col == 9))
            spaceType = 5;
        % Make adjacent spaces 1
        elseif(((row == 3 || row == 8) && (col == 2 || col == 9)) || ((row == 2 || row == 9) && (col == 3 || col == 8)))   
            spaceType = 1;
        end
    end
end