2

I have very limited experience with regular expressions so I'm hoping someone can help me out.

I'm making a Python 3 game that has a rectangular grid as the board. I'm trying to make a way for users to enter several board coordinates at once in the following form into a string called coords:

(x1, y1), (x2, y2), (x3, y3), ... (xn, yn)

I want the output to be a list of tuples called cells in a similar form:

[(x1, y1), (x2, y2), (x3, y3), ... (xn, yn)]

So essentially I want to mimic how tuples can be written in Python code.

Right now I am using:

cells = [tuple(coords[i.find('(') + 1: i.rfind(')')].split(',')) for i in coords.split()]

which produces the desired result for inputs in the form (1,2) (3,4) (5,6), with no spaces inside the inputted tuples and spaces between the tuples. However, this breaks for inputs not following exactly that form, and it does nothing to check for valid inputs. For every x- and y-value in the tuples in cells, I need to validate that:

  1. type(y-value) == type(x-value) == int
  2. 0 <= y-value < game_board.height
  3. 0 <= x-value < game_board.width

Ideally, if the users enters multiple valid coordinates and some invalid ones, the valid tuples would be added to cells and the user would be given a message like "The following coordinates were invalid: (x1, y1), ...".

I know I could do this all with a mess of loops and flow control, but is there a more Pythonic way to accomplish this with regex?

Edit: spelling

5
  • Can you demonstrate how these coordinates are entered in? Is it one-by-one, or are they able to enter a long string that you then parse? Commented Nov 21, 2018 at 19:57
  • 2
    I don't think using a regex would be very pythonic, it'd just be regexic. Commented Nov 21, 2018 at 19:58
  • @Dom Just edited to clarify. The user enters all tuples at once as a long string. Commented Nov 21, 2018 at 19:59
  • Wouldn't a .strip() on the string get rid of the whitespace that's giving you an issue? Commented Nov 21, 2018 at 20:06
  • @Dom The whitespace isn't an issue. I want the user to be able to enter spaces after commas (inside the tuples or separating the tuples) or not. This makes it so I can't split on spaces though, since I don't know where the user will enter spaces when I give them this flexibility. Commented Nov 21, 2018 at 20:11

1 Answer 1

2

Regex is used to test the overall structure - the rest is done w/o regex:

inp = "(3, 4), (a, 7), (-3, 3), (3, 3)"

def MaybeInt(n):
    """Returns an int if possible, else the original value."""
    try:
        return int(n)
    except:
        return n

def inX(n):
    """True if inside X of board."""
    return 0<=n<5

def inY(n):
    """True if inside Y of board."""
    return 0<=n<5

def structOk(t):
    import re
    return re.match(r'^\s*([^,]+,[^,]+\)\s*(?:,\s*\([^,]+,[^,]+\))*)\s*$',t)

def validate(t):
    t = t.replace(" ","")
    if not structOk(t):
        raise ValueError("Structually unsound - try again: "+t)

    k = [ x.lstrip("(") for x in t.replace(" ","").rstrip(")").split("),")]
    tups = [ tuple(map(MaybeInt,tu.split(","))) for tu in k]

    # our "decider" for whats valid/invalid which is used to divide the tuples
    # into our valid/invalid return list's
    test = lambda q: all(isinstance(num,int) for num in q) and inX(q[0]) and inY(q[1])


    rv = [[],[]]  # prepare and append by our decider test
    for k in tups:
        # True == 1, False == 0
        rv[1-test(k)].append(k)

    return rv

valid, invalid = validate(inp)

print(valid)
print(invalid)

Output:

[(3, 4), (3, 3)]    # valid
[('a', 7), (-3, 3)] # invalid

See https://regex101.com/r/OrHGaR/1 for the regex and detailed explanation.

Short description: Its looking for ( ... , ...) with ... not being , -you can refine it by using f.e. [1234567890a-zA-Z] instead of [^,] but then its going to ValueError sooner then later.

Sign up to request clarification or add additional context in comments.

7 Comments

Works exactly how I wanted it. Thanks a lot.
Actually, it seems that your regex expression only matches numbers 0-9. I need to match any value n with 0 < n <= board.size. I tried refining the bracketed part as you suggested but that seems to break the whole thing. How can I fix this?
@JacksonH did you leave the quantifier? [^,]+ ... means anything but a comma for 1 to n times ... the + is a quantifier... if you replace [^,]+ with [1234567890]+ you get 1 to many numbers .. also 000000 would be an option, but thats what you can figure out with regex101.com by testing the regex against the ugliest user input data you can come up with
the boardsize is checked in def isX(n) and def isY(n) .. not in the regex itself. - the regex just checks the overall structure of the input for brackets and comma placements
Inputting "(30, 4a)" also gives Structurally unsound. Shouldn't [^,] be matching "4a"?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.