5

First of all, I am totally new in Python, having just started to learn it. I know a lot of stuff about C++ however and I am just trying to implement some of those in Python.

I have done quite a search on it but I couldn't find any solution that fits my requirement. Please see the following code,

import os

class _Getch:
    """Gets a single character from standard input.  Does not echo to the
screen."""
    def __init__(self):
    try:
        self.impl = _GetchWindows()
    except:
        print("Error!")
    def __call__(self): return self.impl()

class _GetchWindows:
    def __init__(self):
        import msvcrt

    def __call__(self):
        import msvcrt
        return msvcrt.getch()



def mainfun():
    check = fh = True
    while check:
        fh = True
        arr = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
        print ("Welcome to Tic Tac Toe Game!!!\n\n")
        print("Enter 1 to Start Game")
        print("Enter 2 to Exit Game")
        a = _Getch()
        if a == "1":
            while fh:
                os.system("cls")
                drawboard()
                playermove()
                fh = checkresult()
        elif a == "2":
            break
       

As you can see, What I am trying to do here is asking the user to press a number from 1 and 2 and then store that number in "a" and then use it for my requirements.

Now, I first tried using this,

    input('').split(" ")[0]

But this didn't work. It required me to always Press Enter after having typed 1 or 2. So, that didn't work.

Then I found this class of Getch and I implemented it. Long story short, it entered me into a never ending loop and my result is something like this now,

Welcome to Tic Tac Toe Game!!!


Enter 1 to Start Game
Enter 2 to Exit Game

Press Enter to Continue....
Welcome to Tic Tac Toe Game!!!


Enter 1 to Start Game
Enter 2 to Exit Game

Press Enter to Continue....
Welcome to Tic Tac Toe Game!!!


Enter 1 to Start Game
Enter 2 to Exit Game

Press Enter to Continue....

And it is a never ending loop... Even if I press any key like "1" or "2", it still doesn't stop and keep on performing this and don't enter any function.

What I want is a function similar to this,

  1. It should work on PYCHARM Console (I am practicing and I don't want to practice on Terminal. I am used to using the console of the IDE I am working on)
  2. It pauses and waits for the user to enter any input (like input does)
  3. It accepts and stores the very first key entered by the user into the variable. Like in this case, if user presses "1" then it should store that character in "a" and simply move on. You don't have to Press "ENTER" to move on.
  4. If the user presses any other button like "a" or "b" or anything like this, it will simply not do anything and keep on asking for the input until the required number "1" or "2" is entered (and I think that can very easily be handled in this while loop)

In other words, I just want an alternative to getch() command of C++ in Python. I have tried a lot to find it, but I couldn't. Please refer to me a question which provides a solution to this exact question or provide a solution here. Thank you.

Edit: Please note this isn't the complete code. I have only provided the code which is relevant. If anyone needs to see the whole code, I am happy to share that as well.

Complete code is as follows,

import os
import keyboard
def getch():
    alphabet =  list(map(chr, range(97, 123)))
    while True:
        for letter in alphabet: # detect when a letter is pressed
            if keyboard.is_pressed(letter):
                return letter
        for num in range(10): # detect numbers 0-9
            if keyboard.is_pressed(str(num)):
                return str(num)

arr = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
playerturn = 1
def drawboard():
    global playerturn
    print("Player 1 (X) - Player 2 (O)\n")
    print("Turn: Player " + str(playerturn))
    print("\n")
    for i in range(3):
        print (" ", end='')
        for j in range(3):
            print(arr[i][j], end='')
            if j == 2:
                continue
            print("  | ", end='')
        if i == 2:
            continue
        print("")
        print("____|____|____")
        print("    |    |    ")

def playermove():
    global playerturn
    row = col = 0
    correctmove = False
    print("\n\nMake your Move!\n")
    while not correctmove:
        row = int(input("Enter Row: "))
        col = int(input("Enter Col: "))
        if (3 > row > -1) and (-1 < col < 3):
            for i in range(3):
                for j in range(3):
                    if arr[row][col] == 0:
                        correctmove = True
                        if playerturn == 1:
                            arr[row][col] = 1
                        else:
                            arr[row][col] = 2
                        playerturn += 1
                        if playerturn > 2:
                            playerturn = 1


        if not correctmove:
            print ("Wrong Inputs, please enter again, ")

def checkwin():
    for player in range(1, 3):
        for i in range(3):
            if arr[i][0] == player and arr[i][1] == player and arr[i][2] == player: return player
            if arr[0][i] == player and arr[1][i] == player and arr[2][i] == player: return player
        if arr[0][0] == player and arr[1][1] == player and arr[2][2] == player: return player
        if arr[0][2] == player and arr[1][1] == player and arr[2][0] == player: return player
    return -1

def checkdraw():
    for i in range(3):
        for j in range(3):
            if arr[i][j] == 0:
                return False
    return True



def checkresult():
    check = checkwin()
    if check == 1:
        os.system('cls')
        drawboard()
        print("\n\nPlayer 1 has won the game!!\n")
    elif check == 2:
        os.system('cls')
        drawboard()
        print("\n\nPlayer 2 has won the game!!\n")
    elif check == 3:
        os.system('cls')
        drawboard()
        print("\n\nThe game has been drawn!!\n")
    else:
        return True
    return False

def mainfun():
    check = fh = True
    while check:
        os.system("cls")
        fh = True
        arr = [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
        print ("Welcome to Tic Tac Toe Game!!!\n\n")
        print("Enter 1 to Start Game")
        print("Enter 2 to Exit Game")
        a = getch()
        if a == "1":
            while fh:
                os.system("cls")
                drawboard()
                playermove()
                fh = checkresult()
        elif a == "2":
            break
        print ("Press any key to continue...")
        getch()

mainfun()

EDIT2: Problem is resolved by using Keyboard module... The next issue here is how do I remove the data stored in input buffer after the getch() function is called? Because the data in buffer is getting displayed on the next input (when I am taking in the row and column) and I don't want that to happen. I fonud a solution for Linux but not for Windows (or for Pycharm)

Sakib Khan
  • 305
  • 2
  • 13
  • Does this answer your question? [Python read a single character from the user](https://stackoverflow.com/questions/510357/python-read-a-single-character-from-the-user) – WyattBlue Jun 28 '20 at 04:00
  • @WyattBlue no it didn't, the solution provided in the thread is exactly the same as I have shared above. – Sakib Khan Jun 28 '20 at 05:00

2 Answers2

4

It looks like this feature isn't in the standard python library but you can recreate it.

First, install the module 'keyboard'

 $ pip3 install keyboard

Then you can use keyboard.is_pressed() to see if any one character is pressed.

import keyboard  # using module keyboard
import string # use this to get the alphabet

print("Input a character")

def getch():
    alphabet = list(string.ascii_lowercase)
    while True:
        for letter in alphabet: # detect when a letter is pressed
            if keyboard.is_pressed(letter):
                return letter
        for num in range(10): # detect numbers 0-9
            if keyboard.is_pressed(str(num)):
                return str(num)

answer = getch()

print("you choose " + answer)

Edit: For unix you need to run with the script with sudo. This code should work fine on windows.

WyattBlue
  • 591
  • 1
  • 5
  • 21
  • 1
    This worked for me, thank you so much! Just two issues here, if you can resolve, I would be very thankful. First of all, it keeps the number that I press in the buffer (like 1) and in the next input call, it displays those numbers. I don't want that, I want to clear the buffer right after calling getch(). How do I do that? I found a solution for Linux but not for Windows. Secondly, string.ascii_lowercase didnt work. I instead inserted 26 alphabets but is there any alternative to include all ASCII values, like ASCII for "/", etc? Current program serves my purpose but i'm just wondering – Sakib Khan Jun 28 '20 at 05:03
  • I think `list(map(chr, range(97, 123)))` might work if `list(string.ascii_lowercase)` isn't doing right. Unfortunately, I haven't got a solution for removing the users input character at the terminal. – WyattBlue Jun 28 '20 at 05:14
  • Worked perfectly, thank you! This answer is the actual answer to my question, just waiting for somebody to tell about the buffer. Thanks a lot for all the help! Really appreciate it – Sakib Khan Jun 28 '20 at 05:43
-1

For EDIT-2: Use below code to flush the screen:

sys.stdout.flush()
Astik Gabani
  • 599
  • 1
  • 4
  • 11
  • hmm. I tried running your code, but I got an AttributeError. "_Getch instance has no attribute 'impl'" – WyattBlue Jun 28 '20 at 03:35
  • Thanks, @WyattBlue. Can you please give the full traceback? – Astik Gabani Jun 28 '20 at 03:47
  • Sure ```terminal Welcome to Tic Tac Toe Game!!! Enter 1 to Start Game Enter 2 to Exit Game Error! Traceback (most recent call last): File "myKey.py", line 41, in mainfun() File "myKey.py", line 30, in mainfun a = _Getch()() File "myKey.py", line 11, in __call__ def __call__(self): return self.impl() AttributeError: '_Getch' object has no attribute 'impl' ``` – WyattBlue Jun 28 '20 at 03:48
  • @WyattBlue Actual error is still hidden under the try-except block of __init__ method of _Getch class. Can you please remove that. And check once again? – Astik Gabani Jun 28 '20 at 03:52
  • Now I'm getting ModuleNotFoundError: No module named 'msvcrt'. It appears msvcrt is only supported for windows and not any unix system which I am using. https://docs.python.org/3/library/msvcrt.html – WyattBlue Jun 28 '20 at 03:54
  • For Linux, you can refer https://stackoverflow.com/questions/510357/python-read-a-single-character-from-the-user – Astik Gabani Jun 28 '20 at 04:00
  • It didn't work for me.. I tried but now it's not even taking an input. I keep pressing buttons and they are getting printed as it is, wouldn't take the input even if I press "Enter" – Sakib Khan Jun 28 '20 at 05:01
  • Can you describe the action performed by checkresult()? Bcz looping is dependent on that method's return value. – Astik Gabani Jun 28 '20 at 05:12
  • @AstikGabani I have added the complete code at the end of the post now. – Sakib Khan Jun 28 '20 at 05:42
  • @AstikGabani I tried and it gave the same result, nothing different. Still printing those numbers.... I also tried sys.stdin.flush() but it changed nothing. (Just to be clear, to use these functions, I imported the "sys" module) – Sakib Khan Jun 28 '20 at 06:11