A simple, 2-player number guessing game in Python 3.

[[ 🗃 ^yENdo impossible-grid ]] :: [📥 Inbox] [📤 Outbox] [🐤 Followers] [🤝 Collaborators] [🛠 Commits]

Clone

HTTPS: git clone https://vervis.peers.community/repos/yENdo

SSH: git clone USERNAME@vervis.peers.community:yENdo

Branches

Tags

v0.0.2 ::

grid.py

#!/usr/bin/env python3

# Copyright 2018 UltrasonicMadness
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""A grid of numbers of a certain width and height.

The rows and columns on this grid can be highlighted.
"""

from random import randint
from igexceptions import *

class Grid():
    def __init__(self, secretNumber, width, height):
        """Sets up a grid for the Impossible Grid game.
        
        Arrays for storing the numbers of highlighted rows and columns are
        initialised.
        
        Parameters:
        secretNumber: Player 1's secret number.
        width: The width of the grid.
        height: The height of the grid.
        
        Returns:
        This method does not return any data.
        """
        self.secretNumber = secretNumber
        
        self.width = width
        self.height = height
        
        self.hlRows = []
        self.hlCols = []
        
        self.rowHistory = []
        self.colHistory = []
    
    # Returns a horizontal grid-line
    def _genLine(self, start, line, separator, end):
        output = ""
        
        # Generate the top line
        output += start
        
        # For each column in the grid
        for x in range(0, self.width):
            # Make the width of the grid line match the rest of the grid.
            for i in range(0, len(str(self.width * self.height)) + 2):
                output += line
            
            if x < self.width - 1:
                # The separator, which is a T-intersection or a cross
                output += separator
            else:
                # The character at the end (corner or sideways T-intersection)
                output += end
        
        # Return the generated line
        return output
    
    # Returns a horizontal grid line with numbers and separators.
    def _genNumberLine(self, rowNumber, separator, highlight):
        output = ""
        
        # Calculate the first number in this row.
        startingNumber = rowNumber * self.width
        
        # Start with the given separator character
        output += separator
        
        # For each column in the grid
        for x in range(0, self.width):
            # If the cell is highlighted, add the highlight char.
            if x in self.hlCols or rowNumber in self.hlRows:
                hlChar = highlight
            else:
                hlChar = " "
            
            # Output the calculated number in the cell, zero padded to the
            # length of the highest number so the table doesn't look off.
            # It will also be 'highlighted' by the hlChar if the cell is
            # part of a highlighted row or column.
            output += hlChar + str(startingNumber + x + 1) \
                    .zfill(len(str(self.width * self.height))) + hlChar
            
            # Add the last separator char
            output += separator
        
        return output
    
    def show(self):
        """Prints the grid to the standard output.
        
        Returns:
        This method does not return any data.
        """
        # Top line made with box-drawing chars
        print(self._genLine("\u250d", "\u2501", "\u252f", "\u2511"))
        
        # For each row, print the numbers separated by grid lines
        for r in range(0, self.height):
            print(self._genNumberLine(r, "\u2502", "\u2593"))
            
            if r < self.height - 1:
                print(self._genLine("\u251d", "\u2501", "\u253f", "\u2525"))
        
        # Bottom line
        print(self._genLine("\u2515", "\u2501", "\u2537", "\u2519"))

    # The grid calls this function to highlight the nearest valid row
    # to initPos.
    def _cheat(self, initPos, col):
        # How far from the initPos row or column to attempt highlighting
        offset = 1
        
        if col:
            if initPos not in self.colHistory:
                self.colHistory.append(initPos)
        else:
            if initPos not in self.rowHistory:
                self.rowHistory.append(initPos)
        
        while True:
            # Depending on the value, the positions are checked in a different
            # order.
            highFirst = randint(0, 1) == 1
            
            # Order the numbers differently depending on the value generated
            # above.
            if highFirst:
                posToCheck = [initPos + offset, initPos - offset]
            else:
                posToCheck = [initPos - offset, initPos + offset]
            
            # Try both positions in the given order.
            try:
                if col:
                    self.highlightCol(posToCheck[0])
                else:
                    self.highlightRow(posToCheck[0])
                
                break
            except:
                
                try:
                    if col:
                        self.highlightCol(posToCheck[1])
                    else:
                        self.highlightRow(posToCheck[1])
                    
                    break
                except:
                    # If both attempts raise an exception, try again with
                    # the next 2 farther away positions.
                    offset += 1
                

    def highlightRow(self, highlight):
        """Highlights the row at the given position.
        
        Raises:
        HLOutOfRangeException: The position is less than 1 or greater than the
        width.
        DuplicateHLException: The row at the given position has already been
        highlighted.
        """
        if highlight < 0 or highlight > self.width - 1:
            raise HLOutOfRangeException()
        elif highlight in self.hlRows or highlight in self.rowHistory:
            raise DuplicateHLException()
        elif self.rowHasSecretNumber(highlight):
            self._cheat(highlight, False)
        else:
            self.hlRows.append(highlight)
    
    def highlightCol(self, highlight):
        """Highlights the column at the given position.
        
        Raises:
        HLOutOfRangeException: The position is less than 1 or greater than the
        height.
        DuplicateHLException: The column at the given position has already been
        highlighted.
        """
        if highlight < 0 or highlight > self.height - 1:
            raise HLOutOfRangeException()
        elif highlight in self.hlCols or highlight in self.rowHistory:
            raise DuplicateHLException()
        elif self.colHasSecretNumber(highlight):
            self._cheat(highlight, True)
        else:
            self.hlCols.append(highlight)

    def getValueAtPos(self, x, y):
        """Returns the number at the given position on the grid"""
        return self.height * y + x + 1

    def rowHasSecretNumber(self, row):
        """Checks if the given row has the secret number and returns True if
        it does, False otherwise.
        """
        firstNum = self.height * row
        return self.secretNumber in \
                range(firstNum, firstNum + self.width)
    
    def colHasSecretNumber(self, col):
        """Checks if the given column has the secret number and returns True if
        it does, False otherwise.
        """
        firstNum = col
        
        return self.secretNumber in \
                range(firstNum, self.height * self.width, self.height)

[See repo JSON]