Weekly Challenge

Solve the challenge, share your solution and summit the ranks of our Community!
IDEAS WANTED

We're actively looking for ideas on how to improve Weekly Challenges and would love to hear what you think!

Submit Feedback
We've recently made an accessibility improvement to the community and therefore posts without any content are no longer allowed. Please use the spoiler feature or add a short message in the message body in order to submit your weekly challenge.

Challenge #200: Sudoku Solver

Highlighted
7 - Meteor

First challenge .. I did the beginner solution which was good to you learn as you go.

 

I'll leave Intermediate & Advanced for just now.

 

Spoiler
CHerv_0-1592401004138.png

 

Highlighted
7 - Meteor

This was some nice parsing practice. As a beginner, this challenge has taught me I need to get better at RegEx expressions and the tool itself. My solution was very redundant using the formula tool, but hey, that's the beauty of Alteryx, it can be as simple or complex as you need to get the solution.

Highlighted
8 - Asteroid

Never considered myself at the advanced level, so haven't done the second puzzle, but I'm pretty sure if I was good at Macros, I could alter my solution a tiny bit and solve that section too. Maybe one day soon!

Highlighted
11 - Bolide

All three complete.

Spoiler
PaulFound_0-1596554575467.png

PaulFound_1-1596554585551.png 

PaulFound_2-1596554603441.png

I had to use an iterative macro complete the advanced. 
PaulFound_0-1596555288852.png

 

 

Highlighted
Alteryx Certified Partner

What a great way to celebrate Weekly Challenge #200!

 

Sudoku is "a denial of service attack on human intellect"

Ben Laurie


It has been a tremendous journey so far, and I am no way near to have solved 200 challenges myself, but this one deserved some extra effort.

 

I almost feel like writing a blog post on how I did it, but for now you have to take a peak below:

 

Spoiler
I do truly believe that almost everything can be solved in Alteryx one way or another, but I will admit, that the intermediate and advanced puzzle was a too big nut for me to crack

Luckily Alteryx does support the usage of R or Python in this case, so that was my approach here.

Beginner & IntermediateBeginner & Intermediate

 

So the Beginner part was dead easy due to the "hidden" tool Make Columns, and I can never stop mentioning @NicoleJohnson for showing me this tool. Learn it, like it, love it -you find this tool under the Laboratory tab.

Now for the second part the Intermediate, to solve the first beginner puzzle, I immediately thought of using Python, event thought I am not a Python developer, but it seem logic to me, to be a thing thousands had done before in Python.

My initial search brought me to this: Shortest Sudoku Solver in Python - How does it work? on Stack Overflow, and all though the explaining was thoroughly, I didn't make it work as intended, when trying to adapt this into Alteryx

So I researched a little bit further, and found this very nice tutorial Solving Every Sudoku Puzzle by Peter Norvig.
Based on this example, I was able to adapt the logic and put it into Alteryx Python component

#################################
# List all non-standard packages to be imported by your 
# script here (only missing packages will be installed)
from ayx import Package
Package.installPackages(['pandas','numpy'])


#################################
from ayx import Alteryx

df = Alteryx.read("#1")

#print(df.iloc[0,0])

puzzle = df.iloc[0,0]


#################################
## Solve Every Sudoku Puzzle
## See http://norvig.com/sudoku.html

def cross(A, B):
    "Cross product of elements in A and elements in B."
    return [a+b for a in A for b in B]

digits   = '123456789'
rows     = 'ABCDEFGHI'
cols     = digits
squares  = cross(rows, cols)
unitlist = ([cross(rows, c) for c in cols] +
            [cross(r, cols) for r in rows] +
            [cross(rs, cs) for rs in ('ABC','DEF','GHI') for cs in ('123','456','789')])
units = dict((s, [u for u in unitlist if s in u])
             for s in squares)
peers = dict((s, set(sum(units[s],[]))-set([s]))
             for s in squares)

################ Parse a Grid ################

def parse_grid(grid):
    """Convert grid to a dict of possible values, {square: digits}, or
    return False if a contradiction is detected."""
    ## To start, every square can be any digit; then assign values from the grid.
    values = dict((s, digits) for s in squares)
    for s,d in grid_values(grid).items():
        if d in digits and not assign(values, s, d):
            return False ## (Fail if we can't assign d to square s.)
    return values

def grid_values(grid):
    "Convert grid into a dict of {square: char} with '0' or '.' for empties."
    chars = [c for c in grid if c in digits or c in '0.']
    assert len(chars) == 81
    return dict(zip(squares, chars))

################ Constraint Propagation ################

def assign(values, s, d):
    """Eliminate all the other values (except d) from values[s] and propagate.
    Return values, except return False if a contradiction is detected."""
    other_values = values[s].replace(d, '')
    if all(eliminate(values, s, d2) for d2 in other_values):
        return values
    else:
        return False

def eliminate(values, s, d):
    """Eliminate d from values[s]; propagate when values or places <= 2.
    Return values, except return False if a contradiction is detected."""
    if d not in values[s]:
        return values ## Already eliminated
    values[s] = values[s].replace(d,'')
    ## (1) If a square s is reduced to one value d2, then eliminate d2 from the peers.
    if len(values[s]) == 0:
        return False ## Contradiction: removed last value
    elif len(values[s]) == 1:
        d2 = values[s]
        if not all(eliminate(values, s2, d2) for s2 in peers[s]):
            return False
    ## (2) If a unit u is reduced to only one place for a value d, then put it there.
    for u in units[s]:
        dplaces = [s for s in u if d in values[s]]
        if len(dplaces) == 0:
            return False ## Contradiction: no place for this value
        elif len(dplaces) == 1:
            # d can only be in one place in unit; assign it there
            if not assign(values, dplaces[0], d):
                return False
    return values

def string_return(values):
    return "".join(values.values())


#################################
solved = string_return(parse_grid(puzzle))

#print(solved)
#print("Dataframe" , df, sep='\n')

#df.loc[1] = [solved]
df.insert(1,"solutions",[solved], True)

#print("Dataframe" , df, sep='\n')

Alteryx.write(df,1)



#################################

It was not my first rodeo with Alteryx and Python, and for beginners I can highly recommend @SydneyF excellent Tool Mastery on Python 

One of the key things to know, is that when using the Python tool input and output is done in Pandas Data Frame, but once you get at hang of this, it's no biggie.

So if you examine my code (I did mention I am not a Python developer right?)  you can see I do some extraction of the puzzle string and in the end converting the result back into a string I then append to the Pandas Data Frame and output it again,

It this should have been even more nicer, I should have made the logic so you actually could parse multiple puzzles at the same time, and the iterate over them, but oh well - there is always room for improvement.

So using Python did solve the Beginner puzzle okay fast
Python Execution TimePython Execution Time

So the Advanced solution was just to use the same Python code again, and yes, I should obviously have encapsuled this in a macro, since I am using the same code more than once, but apparently I am a slow learner 🙂🙂

Advanced SolutionAdvanced Solution
And apparently, the Python code is not that much longer in solving this harder puzzle
Advanced Python Execution TimeAdvanced Python Execution Time

So If you have read so far, I hope you enjoyed this explanation.

Raise you to the top 😄😄

Still Climbing
/Verakso

 

 

 

Highlighted
7 - Meteor

My Solution for this challenge:

 

1) Beginner level

 

Spoiler
naresh5315_0-1596786748016.png

 

 

2) Intermediate level

 

Spoiler
naresh5315_2-1596786813095.png

 

 

3) Advance level

  • Workflow
Spoiler
naresh5315_4-1596786941242.png
  • Macro
Spoiler
naresh5315_5-1596786979955.png

 

 

 

 

Highlighted
8 - Asteroid
Spoiler
jonathangonzales_0-1599230986223.png

 

Highlighted
8 - Asteroid

Solved Beginner and Intermediate . Good one.ch200.PNG

Highlighted
Alteryx Partner

Phew, got there in the end with the intermediate solution! It's a bit long, so I'll only put a screenshot of the beginner's part 

Spoiler
simbert_0-1600772682383.png

 

Highlighted
Alteryx Partner
Spoiler
challenge_200_jc.png