def possible(s):
    """Make possibility array."""
    from kaidoku.misc import box
    p = [[]] * 81
    b = box()
    for i in range(81):
        if s[i] == 0:
            p[i] = [1] * 9
            for i2 in b[i]:
                if s[i2] > 0:
                    p[i][s[i2] - 1] = 0
        else:
            p[i] = [0] * 9
            p[i][s[i] - 1] = 1
    return (p) def solve(s, verbose, maxdepth, maxtime):
    """Solve a problem."""
    from kaidoku.misc import box
    from kaidoku.misc import line
    from kaidoku.misc import pbox
    solved = False
    p = possible(s)
    b = box()
    pb = pbox()
    linescan = line()
    start = datetime.datetime.now()
    endtime = start + datetime.timedelta(seconds=maxtime)
    LevelPoint = int((blank(s) / 15.0)**3)  # 30 -> 8, 64 -> 77
    while (not solved):
        bl = blank(s)
        s, p, message, logic, depth, found, err = solveone(
            s, p, verbose, 0, maxdepth, endtime, b, pb, linescan)
        if err:
            if found:
                return (s, message, 0, True, True)
            else:
                return (s, message, 0, False, True)
        if found:
            if message != '':
                print(message)
            if verbose > 4 and blank(s) < bl:
                print(output(s))
            point = {
                'Naked single': 0,
                'Hidden single':  3,
                'Pointing pair': 25,
                'Pointing triple': 25,
                'Naked pair': 25,
                'Naked triple': 40,
                'Hidden pair': 50,
                'Hidden triple': 70,
                'X-wing': 70,
                'XY-wing': 70,
                'XYZ-wing': 100,
                'Remote pairs': 100,
                'Naked quad': 120,
                'Hidden quad': 150,
                'Swordfish': 150,
                'Jellyfish': 200,
                'Chain of pairs': 200,
                'Chain of pairs (long)': 300,
                'Trial': 500,
                'Trial (deep)': 1000,
                'Search': 1500,
                'Deep search': 2500
            }[logic]
            if point > 800:
                point = point * (0.5 + 0.02 * blank(s))
            LevelPoint += point
        else:
            return (s, 'Give up', 0, False, False)
        if blank(s) == 0:
            solved = True
        else:
            solved = False
    for i in range(9):
        if [0, 8, 45, 70, 120, 320, 1000, 2000, 5000][i] <= LevelPoint:
            level = i + 1
    return (s, 'Solved', level, True, False) def solveone(s, p, verbose, depth, maxdepth, endtime, b, pb, linescan):
    """Solve one move."""
    from kaidoku.chain import pairchain
    from kaidoku.chain import remotepair
    from kaidoku.misc import boxlist
    from kaidoku.misc import combmir
    from kaidoku.misc import pairs
    from kaidoku.misc import wingpos
    from kaidoku.search import search
    from kaidoku.search import trial
    from kaidoku.wing import xwing
    from kaidoku.wing import xywing
    from kaidoku.wing import xyzwing
    # Check validity
    message, err = check(s)
    if verbose == 0:
        message = ''
    if err:
        return (s, p, message, 0, depth, False, True)

    # Basic logic
    boxl = boxlist(s)
    s, p, message, found, err = naksing(s, p, b, verbose)  # Naked single
    if found or err:
        return (s, p, message, 'Naked single', depth, found, err)
    s, p, message, found, err = hidsing(
        s, p, linescan, verbose)  # Hidden single
    if found or err:
        return (s, p, message, 'Hidden single', depth, found, err)

    # Advanced logic

    # Pointing pair or triple
    s, p, logic, message, found, err = pointing(
        s, p, pb, verbose)
    if found or err:
        return (s, p, message, logic, depth, found, err)

    # Naked pair and triple
    comb, mirror = combmir(p, boxl)
    if depth < 2:
        s, p, message, found, err = naked(
            s, p, 2, boxl, comb, verbose)  # Naked pair
        if found or err:
            return (s, p, message, 'Naked pair', depth, found, err)
    if depth < 1:
        s, p, message, found, err = naked(
            s, p, 3, boxl, comb, verbose)  # Naked triple
        if found or err:
            return (s, p, message, 'Naked triple', depth, found, err)

    # Hidden pair and triple
    if depth < 2:
        s, p, message, found, err = hidden(
            s, p, 2, boxl, mirror, verbose)  # Hidden pair
        if found or err:
            return (s, p, message, 'Hidden pair', depth, found, err)
    if depth < 1:
        s, p, message, found, err = hidden(
            s, p, 3, boxl, mirror, verbose)  # Hidden triple
        if found or err:
            return (s, p, message, 'Hidden triple', depth, found, err)

    # Wing families
    wing = wingpos(boxl, mirror)
    pair, paircomb, pairdict = pairs(s, p)
    if depth < 2:
        s, p, message, found, err = xwing(
            s, p, 2, wing, verbose)  # X-wing
        if found or err:
            return (s, p, message, 'X-wing', depth, found, err)
        s, p, message, found, err = xywing(s, p, b, pair, verbose)  # XY-wing
        if found or err:
            return (s, p, message, 'XY-wing', depth, found, err)
        s, p, message, found, err = xyzwing(
            s, p, b, boxl, comb, pb, verbose)  # XYZ-wing
        if found or err:
            return (s, p, message, 'XYZ-wing', depth, found, err)

    if depth == 0:
        # Remote pairs
        if len(pair) > 3:
            s, p, message, found, err = remotepair(
                s, p, b, pair, pairdict, verbose)  # Remote pairs
            if found or err:
                return (s, p, message, 'Remote pairs', depth, found, err)

        # Naked and hidden quad
        s, p, message, found, err = naked(
            s, p, 4, boxl, comb, verbose)  # Naked quad
        if found or err:
            return (s, p, message, 'Naked quad', depth, found, err)

        s, p, message, found, err = hidden(
            s, p, 4, boxl, mirror, verbose)  # Hidden quad
        if found or err:
            return (s, p, message, 'Hidden quad', depth, found, err)

        # Swordfish and Jellyfish
        s, p, message, found, err = xwing(
            s, p, 3, wing, verbose)  # Swordfish
        if found or err:
            return (s, p, message, 'Swordfish', depth, found, err)
        s, p, message, found, err = xwing(
            s, p, 4, wing, verbose)  # Jellyfish
        if found or err:
            return (s, p, message, 'Jellyfish', depth, found, err)

        # Chain of pairs
        if len(pair) > 3:
            s, p, message, chainlength, found, err = pairchain(
                s, p, b, pair, paircomb, verbose)  # Chain of pairs
            if chainlength < 6:
                logic = 'Chain of pairs'  # Short chain
            else:
                logic = 'Chain of pairs (long)'  # Long chain
            if found or err:
                return (s, p, message, logic, depth, found, err)

    # Trial
    # Maximum steps of scan distinguished from full search of 1 depth
    maxstep = 15
    mincell = 100
    if depth == 0 and maxdepth < 999:
        s, p, step, mincell, message, found, err = trial(
            s, p, linescan, maxstep, verbose, b, pair)
        if step < 10:
            logic = 'Trial'
        else:
            logic = 'Trial (deep)'
        if found or err:
            return (s, p, message, logic, depth, found, err)

    # Search from minimum depth
    if maxdepth == 999:
        bla = blank(s)  # Creating mode
    else:
        bla = 0

    if depth == 0:
        if maxdepth > 2 and bla < 60:
            s, p, message, logic, depth2, found, err = search(
                s, p, verbose, 1, 2, endtime, b, pb, linescan, mincell)
            if found or err:
                return (s, p, message, logic, depth2, found, err)
        if maxdepth > 3 and bla < 45:
            s, p, message, logic, depth2, found, err = search(
                s, p, verbose, 1, 3, endtime, b, pb, linescan, mincell)
            if found or err:
                return (s, p, message, logic, depth2, found, err)
        if maxdepth > 4 and bla < 35:
            s, p, message, logic, depth2, found, err = search(
                s, p, verbose, 1, 4, endtime, b, pb, linescan, mincell)
            if found or err:
                return (s, p, message, logic, depth2, found, err)

    # Full search
    if maxdepth > depth:
        s, p, message, logic, depth, found, err = search(
            s, p, verbose, depth + 1, maxdepth, endtime, b, pb, linescan, mincell)
        if found or err:
            return (s, p, message, logic, depth, found, err)

    return (s, p, '', 0, depth, False, False) def analyze(file, level, verbose):
    """Analyze whole database of the level."""
    if file == '':
        return
    start = datetime.datetime.now()
    input = open(file, 'r')
    i = []
    le = []
    n = 0
    for line in input:
        n += 1
        data = line.strip().split(' ')
        if int(data[0]) == level:
            s = conv(data[1])[0]
            s2 = copy.copy(s)
            if verbose > 0:
                print('Solving problem No.{0}'.format(len(i) + 1))
            s2, message, level2, solved, err = solve(s2, verbose, 20, 10)
            if not solved and err:
                print('\nNo solution {0}'.format(data[1]))
            if solved and err:
                print('\nMultiple solutions {0}'.format(data[1]))
            if not solved and not err:
                print('\nGive up {0}'.format(data[1]))
            i.append(blank(s))
            le.append(level2)
            if verbose == 0:
                if len(i) % 10 == 0:
                    print('.', end='', flush=True)
                if len(i) % 200 == 0:
                    dt = datetime.datetime.now() - start
                    t = dt.seconds + float(dt.microseconds) / 1000000
                    print(' {0} problems solved in {1:.1f} seconds (mean: {2:.3f} sec).'.format(
                        len(i), t, t / len(i)))

    dt = datetime.datetime.now() - start
    t = dt.seconds + float(dt.microseconds) / 1000000
    if len(i) == 0:
        input.close
        return
    print('\n{0} problems solved in {1:.1f} seconds (mean: {2:.3f} sec).'.format(
        len(i), t, t / (len(i))))
    for j in range(11):
        n = le.count(j)
        if n > 0:
            print('Level {0}: {1}'.format(j, n))
    print('Numbers: mean {0:.1f} min {1}'.format(
        81 - mean(i), 81 - max(i)))
    input.close
    return def append_database(file, datadir, giveup, n, creation):
    """Create new problems and append to database."""
    minlevel = int(creation['minlevel'])
    maxtime = 2
    maxdepth = 25

    outfile = openappend(file)
    start = datetime.datetime.now()
    for i in range(n):
        level = 0
        while level < minlevel:
            s, level = create(maxdepth,  maxtime, creation)
            sudoku = short(s)
            if level == 0:
                give = openappend(giveup)
                if give != 'error':
                    give.write('g ' + str(maxtime) + ' ' + sudoku + '\n')
                    give.close()
            else:
                if level >= minlevel:
                    print('.', end='', flush=True)
                    outfile = open(file, 'a')
                    outfile.write(str(level) + ' ' + sudoku + '\n')
                    outfile.flush()
                    outfile.close()
        if (i + 1) % 10 == 0 and i + 1 < n:
            dt = datetime.datetime.now() - start
            t = dt.seconds + float(dt.microseconds) / 1000000
            print(' {0} problems created in {1:.1f} seconds (mean: {2:.3f} sec).'.format(
                i + 1, t, t / (i + 1)))
    dt = datetime.datetime.now() - start
    t = dt.seconds + float(dt.microseconds) / 1000000
    print('\nFinished. {0} problems created in {1:.1f} seconds (mean: {2:.3f} sec).'.format(
        n, t, t / n))
    show_status(file)
    return def merge(file, file2):
    """Import file2 to file."""
    if file == '':
        return
    if file2 == '':
        return
    input = open(file, 'r')
    problem = []
    for line in input:
        data = line.strip().split(' ')
        problem.append(data[1])
    input.close
    n = countproblem(file2)
    print('Importing {0} problems.'.format(n))
    input = open(file2, 'r')
    j = 0
    k = 0
    outfile = openappend(file)
    if outfile == 'error':
        print('Unable to write a file:', file)
        return
    for line in input:
        k += 1
        data = line.strip().split(' ')
        if data[1] not in problem:
            j += 1
            outfile.write(line)
    input.close
    outfile.close
    print('{0} / {1} problems appended.'.format(j, k))
    return def reanalyze(file, file2):
    """Reanalyze whole database."""
    if file == '':
        return
    start = datetime.datetime.now()
    input = open(file, 'r')
    outfile = open(file2, 'w')
    i = 0
    for line in input:
        data = line.strip().split(' ')
        s = conv(data[1])[0]
        s2 = copy.copy(s)
        s2, message, level, solved, err = solve(s2, 0, 20, 10)
        if not solved and err:
            print('\nNo solution {0}'.format(data[1]))
            continue
        if solved and err:
            print('\nMultiple solutions {0}'.format(data[1]))
            continue
        if not solved and not err:
            print('\nGive up {0}'.format(data[1]))
            continue
        outfile.write(str(level) + ' ' + data[1] + '\n')
        dt = datetime.datetime.now() - start
        t = dt.seconds + float(dt.microseconds) / 1000000
        i += 1
        if i % 10 == 0:
            print('.', end='', flush=True)
        if i % 200 == 0:
            dt = datetime.datetime.now() - start
            t = dt.seconds + float(dt.microseconds) / 1000000
            print(' {0} problems reanalyzed in {1:.1f} seconds (mean: {2:.3f} sec).'.format(
                i, t, t / i))
    input.close
    outfile.close
    print('')
    return def reanalyze_giveup(giveup, t):
    """Reanalyze giveup data."""
    give = givedata(giveup, t)
    for i in give[4]:
        print('Invalid data: ', i)
    if len(give[3]) == 0:
        print('No position to analyze.')
        return
    print('Reanalyzing {0} giveup positions for {1} seconds.'.format(
        len(give[3]), t))
    while True:
        give = givedata(giveup, t)
        if len(give[3]) == 0:
            break
        prob = give[3][0][1]
        err = True
        while err:
            s, err = conv(prob)
            if err:
                give = givedata(giveup, t)
                give[3] = give[3][1:]
                writegive(giveup, give)
        print('Analyzing ... ', end='', flush=True)
        start = datetime.datetime.now()
        dt = datetime.datetime.now() - start
        maxdepth = blank(s)
        s, message, level, solved, err = solve(s, 0, maxdepth, t)
        if solved:
            if err:  # Discard multiple solutions
                give = givedata(giveup, t)
                give[3] = give[3][1:]
                writegive(giveup, give)
                print('multiple solutions. {0} left.'.format(len(give[3])))
            else:
                give = givedata(giveup, t)
                give[3] = give[3][1:]
                give[0].append('u ' + str(dt.seconds) + ' ' + prob)
                writegive(giveup, give)
                print('unique solution. {0} left.'.format(len(give[3])))
        else:
            if err:
                give = givedata(giveup, t)
                give[3] = give[3][1:]
                if dt.seconds > 3:
                    give[1].append('n ' + str(dt.seconds) + ' ' + prob)
                writegive(giveup, give)
                print('no solution. {0} left.'.format(len(give[3])))
            else:
                give = givedata(giveup, t)
                give[3] = give[3][1:]
                give[2].append('g ' + str(t) + ' ' + prob)
                writegive(giveup, give)
                print('give up. {0} left.'.format(len(give[3])))
    return def show_status(file):
    """Show status of the database."""
    if file == '':
        return
    input = open(file, 'r')
    li = [0] * 10
    for line in input:
        data = line.strip().split(' ')
        li[int(data[0])] += 1
    su = sum(li)
    if su == 0:
        print('No problem in the book.')
        print('Create problems with c command.')
        return
    for i in range(1, 10):
        print('Level {0} {1:<10}: {2:>6} ({3:5.2f} %)'.format(
            i, lev(i), li[i], li[i] / su * 100))
    print('Total numbers     : {0:>6}'.format(su))
    input.close
    return def advancedhelp():
    """Return help message for advanced commands."""
    message = 'a [verb]        : Analyze the current problem\n'
    message += 'ac [verb]       : Analyze from the Current position\n'
    message += 'all [verb]      : analyze all the problems in the current level\n'
    message += 'append pos      : append a position to book\n'
    message += 'ba [label]      : add current position to bookmark\n'
    message += 'bl              : list bookmark\n'
    message += 'bp pos          : add a specified position bookmark\n'
    message += 'br label        : read bookmark\n'
    message += 'check pos [verb]: check a specified position\n'
    message += 'config          : show and edit configuration\n'
    message += 'create [numbers]: create new problems and append to book\n'
    message += 'giveup [sec]    : reanalyze giveup positions\n'
    message += 'html            : see image in web browzer as html\n'
    message += 'jpg             : draw jpeg image\n'
    message += 'jm              : draw jpeg image with mark\n'
    message += 'import          : import file2\n'
    message += 'initial         : go back to initial position\n'
    message += 'reanalyze       : reanalyze whole book\n'
    message += 'solve pos [verb]: solve a specified position\n'
    message += 'sp              : Solve Partially with naked or hidden single\n'
    message += 'u               : show URL of the current position'
    return message def helpmessage():
    """Return help message."""
    message = '246 : In the cell of row 2 column 4, put number 6\n'
    message += 'b   : take Back one move\n'
    message += 'c   : show Current position\n'
    message += 'i   : show hInt for current position\n'
    message += 'bord: show GUI board\n'
    message += 'q   : Quit kaidoku\n\n'
    message += '[Select problem]\n'
    message += 'j num  : Jump to problem num\n'
    message += 'n      : Next problem\n'
    message += 'p      : Previous problem\n'
    message += 'l level: change Level\n'
    message += 'book   : show index of the book\n\n'
    message += '[Help]\n'
    message += 'h  : show this Help\n'
    message += 'ha : show Help for Advanced commands'
    return message def drawimage(s, p, lastmove, label, imgfile, figure, mark):
    """Draw sudoku image."""
    font = figure['font']
    textcolor = figure['color']
    lastcolor = figure['lastcolor']
    if mark:
        size = figure['markedsize']
    else:
        size = figure['normalsize']
    axis = figure['axis']
    if size in scalelist():
        scale = scalelist()[size]
    else:
        size = 'medium'
        scale = 1.5

    blankfile = os.path.abspath(
        os.path.dirname(__file__)) + '/data/blank-' + size
    if not os.path.isfile(blankfile + '.eps'):
        blankimage(blankfile, scale)
    c = Image.open(blankfile + '.eps')
    draw = ImageDraw.Draw(c)
    try:
        tfont = ImageFont.truetype(font, int(16 * scale))
    except Exception:
        print('Font file cannot be loaded: ', font)
        print('Rewrite configuration of "figure: font" with config command.')
        return True
    nfont = ImageFont.truetype(font, int(24 * scale))
    sfont = ImageFont.truetype(font, int(9 * scale))
    if axis == 'numeric':
        draw.text((20 * scale, 0), label, font=tfont, fill='#000')
    else:
        draw.text((20 * scale, 10 * scale), label, font=tfont, fill='#000')
    if axis == 'numeric':
        for i in range(9):
            draw.text((scale, (45 + i * 28.3) * scale), str(i + 1),
                      font=sfont, fill=textcolor)
        for j in range(9):
            draw.text(((24 + j * 28.3) * scale, 21 * scale), str(j + 1),
                      font=sfont, fill=textcolor)
    for i in range(9):
        for j in range(9):
            n = int(s[i * 9 + j])
            if n == 0 and mark:
                for m in range(9):
                    if p[i * 9 + j][m] == 1:
                        draw.text(((16 + j * 28.3 + (m % 3) * 8) * scale,
                                   (36 + i * 28.3 +
                                    (m // 3) * 8) * scale), str(m + 1),
                                  font=sfont, fill=textcolor)
            if n > 0:
                if i * 9 + j == lastmove:
                    color = lastcolor
                else:
                    color = textcolor
                draw.text(((20 + j * 28.3) * scale, (36 + i * 28.3)
                           * scale), str(n), font=nfont, fill=color)
    c.save(imgfile, 'JPEG', quality=100, optimize=True)
    return False def blank(s):
    """Count numbers of blank cells."""
    blank = 0
    for i in range(81):
        if s[i] == 0:
            blank = blank + 1
    return (blank) def box():
    """Define the effective cells."""
    box = []
    for i in range(9):
        for j in range(9):
            b = [0] * 81
            for i2 in range(9):
                b[i2 * 9 + j] = 1
            for j2 in range(9):
                b[i * 9 + j2] = 1
            begin = (i // 3) * 27 + (j // 3) * 3
            for i2 in [0, 1, 2]:
                for j2 in [0, 1, 2]:
                    b[begin + i2 * 9 + j2] = 1
            b[i * 9 + j] = 0
            bb = []
            for i2 in range(81):
                if b[i2] == 1:
                    bb = bb + [i2]
            box = box + [bb]
    return box def check(s):
    """Check if there is no duplicate numbers in same row, column and box."""
    b = box()
    for i in range(81):
        if s[i] > 0:
            for j in b[i]:
                if s[i] == s[j]:
                    return ('Both ' + cell(i) + ' and ' + cell(j)
                            + ' have the same value of ' + str(
                            s[i]) + '.', True)
    return ('', False) def conv(problem):
    """Convert problem to array."""
    s = []
    if problem.find(',') == -1:
        for i in problem:
            if i.isdigit():
                s = s + [int(i)]
            else:
                s = s + [0]
        if len(s) != 81:
            digit = str(abs(len(s)-81)) + ' digit'
            if digit != '1 digit':
                digit += 's'
            if len(s) > 81:
                message = 'Too many digits. Remove ' + digit + '.'
            else:
                message = 'Insufficient digits. Add ' + digit + '.'
            return ('Error in input. ' + message, True)
    else:
        for line in problem.split(","):
            if (len(line)) == 9:
                for i in line:
                    if i.isdigit():
                        s = s + [int(i)]
                    else:
                        s = s + [0]
            else:
                return ('Error in input: ' + line, True)

    if len(s) == 81:
        return (s, False)
    else:
        return ('Error in input', True) def current(s, move):
    """Move to current position."""
    pos = 0
    for m in move:
        m = int(m)
        row = m // 100
        column = (int(m) - row * 100) // 10
        n = m % 10
        i = (row - 1) * 9 + column - 1
        if s[i] > 0:
            message = 'Cell ' + \
                cell(i) + ' is already filled with ' + str(s[i])
            if pos == 0:
                return (s, [], message, True)
            else:
                return (s, move[:pos], message, True)
        s2 = copy.copy(s)
        s2[i] = n
        message, err = check(s2)
        if err:
            if pos == 0:
                return (s, [], message, True)
            else:
                return (s, move[:pos], message, True)
        message = 'Moved: ' + cell(i) + ' = ' + str(n)
        s = s2
        pos += 1
    return s, move, message, False def duplicate(s1, s2):
    """Find different cells."""
    s = []
    for i in range(81):
        if s1[i] == s2[i]:
            s.append(s1[i])
        else:
            s.append(0)
    return (s) def lev(i):
    """Description of levels."""
    return (['undefined', 'trivial', 'very easy', 'easy', 'normal', 'hard',
             'very hard', 'evil', 'extreme', 'ultimate'][i]) def line():
    """List of lines to scan."""
    line = []
    for b in [0, 3, 6, 27, 30, 33, 54, 57, 60]:
        one = []
        for n in [0, 1, 2, 9, 10, 11, 18, 19, 20]:
            one.append(b + n)
        line.append(one)
    for i in range(9):
        one = []
        for j in range(9):
            one.append(i * 9 + j)
        line.append(one)
    for j in range(9):
        one = []
        for i in range(9):
            one.append(i * 9 + j)
        line.append(one)
    return line def openappend(file):
    """Open as append if exists, write if not."""
    try:
        output = open(file, 'a')
    except Exception:
        try:
            output = open(file, 'w')
        except Exception:
            return 'error'
    return output def pbox():
    """List for use in calculating pointing pair and triple."""
    pbox = {}
    for b in range(9):
        begin = [0, 3, 6, 27, 30, 33, 54, 57, 60][b]
        left = [0, 0, 0, 27, 27, 27, 54, 54, 54][b]
        top = [0, 3, 6, 0, 3, 6, 0, 3, 6][b]
        box = set([])
        for i in (0, 1, 2, 9, 10, 11, 18, 19, 20):
            box = box | set([begin + i])
        for j in range(3):
            line = set([])
            for i in range(9):
                line = line | set([left + j * 9 + i])
            pbox[(b, j)] = line - box
        for j in range(3, 6):
            line = set([])
            for i in range(9):
                line = line | set([top + j - 3 + i * 9])
            pbox[(b, j)] = line - box
    return pbox def output(s):
    """Convert to text output format."""
    out = '  1 2 3 4 5 6 7 8 9\n'
    out += ' +-----+-----+-----+\n'
    for i in range(9):
        out += str(i + 1) + '|'
        for j in range(9):
            v = s[i * 9 + j]
            if v == 0:
                out += ' '
            else:
                out = out + str(v)
            if j % 3 == 2:
                out += "|"
            else:
                out += ' '
        out += '\n'
        if i % 3 == 2:
            out += ' +-----+-----+-----+\n'
    return out def short(s):
    """Convert to short form."""
    sudoku = ''
    for j in s:
        sudoku += str(j)
    return sudoku def url(s):
    """Convert to URL."""
    out = "http://www.sudoku-solutions.com/index.php?puzzle="
    for i in range(81):
        v = str(s[i])
        if v == 0:
            out = out + '.'
        else:
            out = out + str(v)
    return out class GUIBord(tkinter.Tk):
    def __init__(self):
        super().__init__()

    def display(self,s,move):
        self.title("KAIDOKU")
        self.geometry("270x300")
        self.resizable(0,0)
        self.move = copy.deepcopy(move)

        frame1 = tkinter.Frame(self)
        frame1.grid()

        bordFrame = tkinter.Frame(frame1)
        bordFrame.grid(row=0, column=0)

        # make 3 * 3 blocks
        blockList = [[None]*3 for i in range(3)]
        for i in range(3):
            for j in range(3):
                block = tkinter.Frame(
                    bordFrame,
                    width = 90,
                    height = 90,
                    borderwidth = 1,
                    relief="solid",
                )
                block.grid(row=i, column=j)
                block.grid_propagate(0)
                blockList[i][j] = block
        self.inputList = [[None]*9 for i in range(9)]
        for i in range(9):
            for j in range(9):
                square = tkinter.Frame(
                    blockList[i//3][j//3],
                    width = 30,
                    height = 30,
                    borderwidth = 0,
                    background="white smoke",
                    relief="solid",
                )
                square.propagate(0)
                square.grid(row=i%3, column=j%3)
                # If the value is 0 , create a text box
                if s[i * 9 + j] != 0:
                    label = tkinter.Label(
                        square,
                        text = s[i * 9 + j],
                    )
                    label.pack()
                    self.inputList[i][j] = s[i * 9 + j]
                else:
                    textBox = tkinter.Entry(
                        square,
                        background = "white smoke",
                        justify = "center",
                        validate = 'key',
                        vcmd = (self.register(self.validation),'%P',i,j)
                    )
                    textBox.pack()
                    self.inputList[i][j] = textBox
        for v in move:
            idx = int(v[0]) - 1;jdx = int(v[1]) - 1
            nval = v[2]
            self.inputList[idx][jdx].insert(0,nval)
        # set button
        buttonFrame = tkinter.Frame(frame1)
        buttonFrame.grid(row=1, column=0)
        submitButton = tkinter.Button(
            buttonFrame,
            text="save",
            command=self.saveClick,
        )
        submitButton.pack()
        self.mainloop()

    """When user click save-button"""
    def saveClick(self):
        numList = [[None]*9 for i in range(9)]
        self.move = []
        for i in range(9):
            for j in range(9):
                if type(self.inputList[i][j]) != int:
                    if self.inputList[i][j].get() == '':continue
                    self.move.append(str(i + 1) + str(j + 1) + self.inputList[i][j].get())

    def validation(self,P,idx,jdx):
        idx = int(idx);jdx = int(jdx)
        if len(P) == 0:return True
        if len(P) > 1:return False
        if not (ord('1') <= ord(P) <= ord('9')):return False
        # If input value get stuck in a constraint , return False
        for i in range(9):
            if self.objToStr(self.inputList[idx][i]) == P:return False
            if self.objToStr(self.inputList[i][jdx]) == P:return False
            if self.objToStr(self.inputList[(idx//3)*3+i//3][(jdx//3)*3+i%3]) == P:return False
        return True

    def objToStr(self,obj):
        if type(obj) == int:return str(obj)
        if type(obj) == str:return obj 
        return obj.get()
import copy
import datetime
import os.path
    import shutil
    import webbrowser
from configobj import ConfigObj
from kaidoku.calc import possible
from kaidoku.calc import solve
from kaidoku.calc import solveone
from kaidoku.create import analyze
from kaidoku.create import append_database
from kaidoku.create import merge
from kaidoku.create import reanalyze
from kaidoku.create import reanalyze_giveup
from kaidoku.create import show_status
from kaidoku.help import advancedhelp
from kaidoku.help import helpmessage
from kaidoku.image import drawimage
from kaidoku.misc import blank
from kaidoku.misc import box
from kaidoku.misc import check
from kaidoku.misc import conv
from kaidoku.misc import current
from kaidoku.misc import duplicate
from kaidoku.misc import lev
from kaidoku.misc import line
from kaidoku.misc import openappend
from kaidoku.misc import pbox
from kaidoku.output import output
from kaidoku.output import short
from kaidoku.output import url
from kaidoku.gui import GUIBord
        from kaidoku.test import test_all
        from kaidoku.image import blankimages
        s2, message, level2, solved, err = solve(s2, 0, blank(s), maxtime)
        if err:
            if solved:
                print('This position has multiple solutions.')
            else:
                print(
                    'There is no solution for this position. '
                    + 'You can take back one move with b.')
            return config, True
        p = possible(s)
        b = box()
        pb = pbox()
        blank1 = blank(s)
        linescan = line()
        start = datetime.datetime.now()
        endtime = start + datetime.timedelta(seconds=maxtime)
    if c == 'i' or c == 'ii' or c == 'iii':  # show hint
        s2 = copy.copy(s)
        s2, p, message, logic, depth, found, err = solveone(
            s2, p, 4, 0, blank1, endtime, b, pb, linescan)
        if logic == 'Naked single' or logic == 'Hidden single':
            if logic == 'Naked single':
                print('Look at {0}. What number is available?'.format(
                    message[14:18]))
            else:
                print(message[:message.index(':')] + 'can be found.')
        else:
            if c == 'i':
                print('Think candidates of the cells.')
                if datadir == '':
                    print('Use jm command to see the diagram of candidates.')
                else:
                    datadir = checkdatadir(datadir)
                    config['datadir'] = datadir
                    imgfile = datadir + '/current.jpg'
                    p = possible(s)
                    move = config['move']
                    if len(move) > 0:
                        lastmove = int(move[len(move) - 1]) // 10
                        lastmove = (lastmove // 10 - 1) * 9 + lastmove % 10 - 1
                    else:
                        lastmove = 100
                    figure = config['figure']
                    err = drawimage(s, p, lastmove, label, imgfile,
                                    figure, True)
                    if not err:
                        print('See image by "html".')
                print('For more hints, type ii.')
            if c == 'ii' or c == 'iii':
                logi = [logic]
                mes = [message]
                while blank(s2) == blank1:
                    s2, p, message, logic, depth, found, err = solveone(
                        s2, p, 4, 0, blank1, endtime, b, pb, linescan)
                    logi.append(logic)
                    mes.append(message)
                if c == 'ii':
                    if len(logi) > 1:
                        print('Following logics are successively used.')
                    for i in range(len(logi)):
                        print(logi[i])
                    print('See full explanation by typing iii.')
                else:
                    for message in mes:
                        print(message)
    if c == 'sp':
        logic = 'Naked single'
        while (logic == 'Naked single' or logic == 'Hidden single') and blank(s) > 0:
            s2 = copy.copy(s)
            s, p, message, logic, depth, found, err = solveone(
                s, p, 4, 0, blank(s), endtime, b, pb, linescan)
            if logic == 'Naked single' or logic == 'Hidden single':
                print(message)
                for i in range(81):
                    if s[i] != s2[i]:
                        j = (i // 9) * 10 + i % 9 + 11
                        m = j * 10 + s[i]
                        move.append(m)
            else:
                s = s2
        config, err = show('c', 0, config)
    return config, False


def checkdatadir(datadir):
    """Check data directory."""
    if datadir == '':
        print('Data directory does not exist.')
        while True:
            datadir = input(
                'Input name of the data directory (default: ~/kaidoku): ')
            if datadir == '':
                datadir = os.path.expanduser('~/kaidoku')
            datadir = os.path.expanduser(datadir)
            if not os.path.isdir(datadir):
                try:
                    os.mkdir(datadir)
                    break
                except Exception:
                    print('Error: directory cannot be created.', datadir)
                datadir = ''
            else:
                break
    return datadir


def solveprint(s, verbose, maxdepth, maxtime):
    """Analyze a problem (from a command through show)."""
    s, message, level, solved, err = solve(s, verbose, maxdepth, maxtime)
    if err:
        if verbose > 0:
            print(message)
        if solved:
            if verbose > 0:
                print("Invalid sudoku with multiple solutions.")
            if verbose > 4:
                print('Solution 1.')
                print(output(s[0]))
                print('Solution 2.')
                print(output(s[1]))

                s = duplicate(s[0], s[1])