Example #1
0
def grid_screen():
    """ Renders the grid screen """

    # Get the existing grid from the session
    grid = Grid.from_json(session['grid'])
    gridname = session.get('gridname', None)

    # Create the SVG
    svg = GridToSVG(grid)
    boxsize = svg.boxsize
    svgstr = svg.generate_xml()

    # Set the state to editing grid
    session['uistate'] = UIState.EDITING_GRID
    enabled = session['uistate'].get_enabled()
    enabled["grid_undo"] = len(grid.undo_stack) > 0
    enabled["grid_redo"] = len(grid.redo_stack) > 0
    enabled["grid_delete"] = gridname is not None

    # Show the grid.html screen
    return render_template('grid.html',
                           enabled=enabled,
                           n=grid.n,
                           gridname=gridname,
                           boxsize=boxsize,
                           svgstr=svgstr)
Example #2
0
def puzzle_new():
    """ Creates a new puzzle and redirects to puzzle screen """

    # Get the chosen grid name from the query parameters
    gridname = request.args.get('gridname')

    # Open the corresponding file and read its contents as json
    # and recreate the grid from it
    userid = 1  # TODO replace hard-coded user ID
    query = DBGrid.query.filter_by(userid=userid, gridname=gridname)
    jsonstr = query.first().jsonstr
    grid = Grid.from_json(jsonstr)

    # Now pass this grid to the Puzzle() constructor
    puzzle = Puzzle(grid)

    # Save puzzle in the session
    jsonstr = puzzle.to_json()
    session['puzzle'] = jsonstr
    session['puzzle.initial.sha'] = sha256(jsonstr)

    # Remove any leftover puzzle name in the session
    session.pop('puzzlename', None)

    return redirect(url_for('uipuzzle.puzzle_screen'))
Example #3
0
 def get_bad_grid():
     jsonstr = """
 {
 "n": 7,
 "black_cells": [
 [ 1, 3 ], [ 2, 3 ], [ 3, 3 ], [ 3, 4 ], [ 3, 5 ], [ 4, 2 ],
 [ 4, 6 ], [ 5, 3 ], [ 5, 4 ], [ 5, 5 ], [ 6, 5 ], [ 7, 5 ]
 ],
 "numbered_cells": [
 { "seq": 1, "r": 1, "c": 1, "a": 2, "d": 7 },
 { "seq": 2, "r": 1, "c": 2, "a": 0, "d": 3 },
 { "seq": 3, "r": 1, "c": 4, "a": 4, "d": 2 },
 { "seq": 4, "r": 1, "c": 5, "a": 0, "d": 2 },
 { "seq": 5, "r": 1, "c": 6, "a": 0, "d": 3 },
 { "seq": 6, "r": 1, "c": 7, "a": 0, "d": 7 },
 { "seq": 7, "r": 2, "c": 1, "a": 2, "d": 0 },
 { "seq": 8, "r": 2, "c": 4, "a": 4, "d": 0 },
 { "seq": 9, "r": 3, "c": 1, "a": 2, "d": 0 },
 { "seq": 10, "r": 3, "c": 6, "a": 2, "d": 0 },
 { "seq": 11, "r": 4, "c": 3, "a": 3, "d": 0 },
 { "seq": 12, "r": 5, "c": 1, "a": 2, "d": 0 },
 { "seq": 13, "r": 5, "c": 2, "a": 0, "d": 3 },
 { "seq": 14, "r": 5, "c": 6, "a": 2, "d": 3 },
 { "seq": 15, "r": 6, "c": 1, "a": 4, "d": 0 },
 { "seq": 16, "r": 6, "c": 3, "a": 0, "d": 2 },
 { "seq": 17, "r": 6, "c": 4, "a": 0, "d": 2 },
 { "seq": 18, "r": 6, "c": 6, "a": 2, "d": 0 },
 { "seq": 19, "r": 7, "c": 1, "a": 4, "d": 0 },
 { "seq": 20, "r": 7, "c": 6, "a": 2, "d": 0 }
 ]
 }        
 """
     grid = Grid.from_json(jsonstr)
     return grid
Example #4
0
def grid_redo():
    """ Redoes the last grid action then redirects to grid screen """

    jsonstr = session.get('grid', None)
    grid = Grid.from_json(jsonstr)
    grid.redo()
    jsonstr = grid.to_json()
    session['grid'] = jsonstr
    return redirect(url_for('uigrid.grid_screen'))
Example #5
0
def grid_save_common(gridname):
    """ Common method used by both grid_save and grid_save_as """

    # Recreate the grid from the JSON in the session
    # and validate it
    jsonstr = session.get('grid', None)
    grid = Grid.from_json(jsonstr)
    ok, messages = grid.validate()
    if not ok:
        flash("Grid not saved")
        for message_type in messages:
            message_list = messages[message_type]
            if len(message_list) > 0:
                flash(f"*** {message_type} ***")
                for message in message_list:
                    flash("   " + message)
    else:
        # Save the file
        userid = 1  # TODO Replace hard coded user id
        query = DBGrid.query.filter_by(userid=userid, gridname=gridname)
        if not query.all():
            # No grid in the database. This is an insert
            logging.debug(f"Inserting grid '{gridname}' into grids table")
            created = modified = datetime.now().isoformat()
            newgrid = DBGrid(userid=userid,
                             gridname=gridname,
                             created=created,
                             modified=modified,
                             jsonstr=jsonstr)
            db.session.add(newgrid)
            db.session.commit()
        else:
            # There is a grid. This is an update
            logging.debug(f"Updating grid '{gridname}' in grids table")
            oldgrid = query.first()
            oldgrid.modified = datetime.now().isoformat()
            oldgrid.jsonstr = jsonstr
            db.session.commit()

        # Send message about save
        flash(f"Grid saved as {gridname}")

        # Store the sha256 of the saved version of the grid
        # in the session as 'grid.initial.sha' so that we can detect
        # whether it has been changed since it was last saved
        session['grid.initial.sha'] = sha256(jsonstr)

    # Show the grid screen
    return redirect(url_for('uigrid.grid_screen'))
Example #6
0
def grid_rotate():
    """ Rotates the grid 90 degrees left then returns the new SVG """

    # Rotate the grid
    jsonstr = session.get('grid', None)
    grid = Grid.from_json(jsonstr)
    grid.rotate()

    # Save the updated grid in the session
    session['grid'] = grid.to_json()

    # Send the new SVG data to the client
    svg = GridToSVG(grid)
    svgstr = svg.generate_xml()
    response = make_response(svgstr, HTTPStatus.OK)
    return response
Example #7
0
def grid_statistics():
    """ Return the grid statistics in a JSON string """

    # Get the grid from the session
    grid = Grid.from_json(session['grid'])
    gridname = session.get('gridname', None)
    stats = grid.get_statistics()
    enabled = {}

    svgstr = GridToSVG(grid).generate_xml()

    # Render with grid statistics template
    return render_template("grid-statistics.html",
                           enabled=enabled,
                           gridname=gridname,
                           svgstr=svgstr,
                           stats=stats)
Example #8
0
def grid_new_from_puzzle():
    """ Creates a new grid from the specified puzzle """

    puzzlename = request.args.get('puzzlename')
    userid = 1  # TODO replace hard-coded user ID
    query = DBPuzzle.query.filter_by(userid=userid, puzzlename=puzzlename)
    jsonstr = query.first().jsonstr
    grid = Grid.from_json(jsonstr)
    grid.undo_stack = []
    grid.redo_stack = []
    jsonstr = grid.to_json()

    # Save grid in the session
    session['grid'] = jsonstr
    session['grid.initial.sha'] = sha256(jsonstr)

    # Remove any leftover grid name in the session
    session.pop('gridname', None)

    return redirect(url_for('uigrid.grid_screen'))
Example #9
0
def grid_preview():
    """ Creates a grid preview and returns it to ??? """
    userid = 1  # TODO Replace hard coded user id

    # Get the chosen grid name from the query parameters
    gridname = request.args.get('gridname')

    # Open the corresponding file and read its contents as json
    # and recreate the grid from it
    jsonstr = grid_load_common(userid, gridname)
    grid = Grid.from_json(jsonstr)

    # Get the top two word lengths
    heading_list = [f"{grid.get_word_count()} words"]
    wlens = grid.get_word_lengths()
    wlenkeys = sorted(wlens.keys(), reverse=True)
    wlenkeys = wlenkeys[:min(2, len(wlenkeys))]
    for wlen in wlenkeys:
        entry = wlens[wlen]
        total = 0
        if entry["alist"]:
            total += len(entry["alist"])
        if entry["dlist"]:
            total += len(entry["dlist"])
        heading_list.append(f"{wlen}-letter: {total}")
    heading = f'Grid {gridname}({", ".join(heading_list)})'

    scale = 0.75
    svgobj = GridToSVG(grid, scale=scale)
    width = (svgobj.boxsize * grid.n + 32) * scale
    svgstr = svgobj.generate_xml()

    obj = {
        "gridname": gridname,
        "heading": heading,
        "width": width,
        "svgstr": svgstr
    }
    resp = make_response(json.dumps(obj), HTTPStatus.OK)
    resp.headers['Content-Type'] = "application/json"
    return resp
Example #10
0
def grid_click():
    """ Adds or removes a black cell then returns the new SVG """

    # Get the row and column clicked from the query parms
    r = int(request.args.get('r'))
    c = int(request.args.get('c'))

    # Get the existing grid from the session
    jsonstr = session['grid']
    grid = Grid.from_json(jsonstr)

    # Toggle the black cell status
    if grid.is_black_cell(r, c):
        grid.remove_black_cell(r, c)
    else:
        grid.add_black_cell(r, c)

    # Save the updated grid in the session
    session['grid'] = grid.to_json()

    return redirect(url_for('uigrid.grid_screen'))
Example #11
0
def puzzle_replace_grid():

    # Get the chosen grid name from the query parameters
    gridname = request.args.get('gridname')
    if not gridname:
        return redirect(url_for('uipuzzle.puzzle_screen'))

    # Create the grid
    userid = 1  # TODO replace hard-coded user ID
    query = DBGrid.query.filter_by(userid=userid, gridname=gridname)
    jsonstr = query.first().jsonstr
    grid = Grid.from_json(jsonstr)

    # Get the current puzzle
    puzzle = Puzzle.from_json(session['puzzle'])

    # and replace its grid
    puzzle.replace_grid(grid)

    # Save in the session
    session['puzzle'] = puzzle.to_json()

    return redirect(url_for('uipuzzle.puzzle_screen'))
    def test_new_grid(self):
        puzzle = TestPuzzle.create_solved_atlantic_puzzle()
        oldjson = puzzle.to_json()
        grid = Grid.from_json(puzzle.to_json())
        grid.add_black_cell(4, 4)
        puzzle.replace_grid(grid)
        newjson = puzzle.to_json()

        import json
        old = json.loads(oldjson)
        new = json.loads(newjson)

        # Compare black cells
        self.assertIn([4, 4], new['black_cells'])
        self.assertIn([6, 6], new['black_cells'])
        new['black_cells'].remove([4, 4])
        new['black_cells'].remove([6, 6])
        self.assertListEqual(old['black_cells'], new['black_cells'])

        # Compare numbered cells
        expected = [
            NumberedCell(seq=1, r=1, c=1, a=3, d=4),
            NumberedCell(seq=2, r=1, c=2, a=0, d=4),
            NumberedCell(seq=3, r=1, c=3, a=0, d=9),
            NumberedCell(seq=4, r=1, c=6, a=4, d=5),
            NumberedCell(seq=5, r=1, c=7, a=0, d=9),
            NumberedCell(seq=6, r=1, c=8, a=0, d=4),
            NumberedCell(seq=7, r=1, c=9, a=0, d=3),
            NumberedCell(seq=8, r=2, c=1, a=4, d=0),
            NumberedCell(seq=9, r=2, c=4, a=0, d=2),
            NumberedCell(seq=10, r=2, c=6, a=4, d=0),
            NumberedCell(seq=11, r=3, c=1, a=9, d=0),
            NumberedCell(seq=12, r=3, c=5, a=0, d=5),
            NumberedCell(seq=13, r=4, c=1, a=3, d=0),
            NumberedCell(seq=14, r=4, c=5, a=4, d=0),
            NumberedCell(seq=15, r=5, c=3, a=5, d=0),
            NumberedCell(seq=16, r=5, c=4, a=0, d=5),
            NumberedCell(seq=17, r=6, c=2, a=4, d=4),
            NumberedCell(seq=18, r=6, c=7, a=3, d=0),
            NumberedCell(seq=19, r=6, c=8, a=0, d=4),
            NumberedCell(seq=20, r=6, c=9, a=0, d=4),
            NumberedCell(seq=21, r=7, c=1, a=9, d=3),
            NumberedCell(seq=22, r=7, c=6, a=0, d=2),
            NumberedCell(seq=23, r=8, c=1, a=4, d=0),
            NumberedCell(seq=24, r=8, c=6, a=4, d=0),
            NumberedCell(seq=25, r=9, c=1, a=4, d=0),
            NumberedCell(seq=26, r=9, c=7, a=3, d=0),
        ]
        actual = []
        for x in new['numbered_cells']:
            jsonstr = json.dumps(x)
            nc = NumberedCell.from_json(jsonstr)
            actual.append(nc)
        self.assertListEqual(expected, actual)

        # Compare clues

        oldclues = {
            x['text']: x['clue']
            for x in old['across_words'] + old['down_words']
        }
        newclues = {
            x['text']: x['clue']
            for x in new['across_words'] + new['down_words']
        }
        for k, v in newclues.items():
            if k in oldclues:
                oldclue = oldclues[k]
                self.assertEqual(oldclue, newclues[k])
 def test_same_grid(self):
     oldpuzzle = TestPuzzle.create_solved_atlantic_puzzle()
     grid = Grid.from_json(oldpuzzle.to_json())
     newpuzzle = Puzzle.from_json(oldpuzzle.to_json())
     newpuzzle.replace_grid(grid)
     self.assertEqual(oldpuzzle, newpuzzle)
Example #14
0
    def get_good_grid():
        jsonstr = """
{
  "n": 15,
  "black_cells": [
    [ 1, 4 ],
    [ 1, 5 ],
    [ 1, 11 ],
    [ 2, 5 ],
    [ 2, 11 ],
    [ 3, 5 ],
    [ 3, 11 ],
    [ 4, 1 ],
    [ 4, 2 ],
    [ 4, 8 ],
    [ 4, 14 ],
    [ 4, 15 ],
    [ 5, 1 ],
    [ 5, 2 ],
    [ 5, 3 ],
    [ 5, 7 ],
    [ 5, 8 ],
    [ 5, 13 ],
    [ 5, 14 ],
    [ 5, 15 ],
    [ 6, 8 ],
    [ 6, 9 ],
    [ 6, 10 ],
    [ 7, 5 ],
    [ 7, 11 ],
    [ 8, 5 ],
    [ 8, 11 ],
    [ 9, 5 ],
    [ 9, 11 ],
    [ 10, 6 ],
    [ 10, 7 ],
    [ 10, 8 ],
    [ 11, 1 ],
    [ 11, 2 ],
    [ 11, 3 ],
    [ 11, 8 ],
    [ 11, 9 ],
    [ 11, 13 ],
    [ 11, 14 ],
    [ 11, 15 ],
    [ 12, 1 ],
    [ 12, 2 ],
    [ 12, 8 ],
    [ 12, 14 ],
    [ 12, 15 ],
    [ 13, 5 ],
    [ 13, 11 ],
    [ 14, 5 ],
    [ 14, 11 ],
    [ 15, 5 ],
    [ 15, 11 ],
    [ 15, 12 ]
  ],
  "numbered_cells": [
    { "seq": 1, "r": 1, "c": 1, "a": 3, "d": 3 },
    { "seq": 2, "r": 1, "c": 2, "a": 0, "d": 3 },
    { "seq": 3, "r": 1, "c": 3, "a": 0, "d": 4 },
    { "seq": 4, "r": 1, "c": 6, "a": 5, "d": 9 },
    { "seq": 5, "r": 1, "c": 7, "a": 0, "d": 4 },
    { "seq": 6, "r": 1, "c": 8, "a": 0, "d": 3 },
    { "seq": 7, "r": 1, "c": 9, "a": 0, "d": 5 },
    { "seq": 8, "r": 1, "c": 10, "a": 0, "d": 5 },
    { "seq": 9, "r": 1, "c": 12, "a": 4, "d": 14 },
    { "seq": 10, "r": 1, "c": 13, "a": 0, "d": 4 },
    { "seq": 11, "r": 1, "c": 14, "a": 0, "d": 3 },
    { "seq": 12, "r": 1, "c": 15, "a": 0, "d": 3 },
    { "seq": 13, "r": 2, "c": 1, "a": 4, "d": 0 },
    { "seq": 14, "r": 2, "c": 4, "a": 0, "d": 14 },
    { "seq": 15, "r": 2, "c": 6, "a": 5, "d": 0 },
    { "seq": 16, "r": 2, "c": 12, "a": 4, "d": 0 },
    { "seq": 17, "r": 3, "c": 1, "a": 4, "d": 0 },
    { "seq": 18, "r": 3, "c": 6, "a": 5, "d": 0 },
    { "seq": 19, "r": 3, "c": 12, "a": 4, "d": 0 },
    { "seq": 20, "r": 4, "c": 3, "a": 5, "d": 0 },
    { "seq": 21, "r": 4, "c": 5, "a": 0, "d": 3 },
    { "seq": 22, "r": 4, "c": 9, "a": 5, "d": 0 },
    { "seq": 23, "r": 4, "c": 11, "a": 0, "d": 3 },
    { "seq": 24, "r": 5, "c": 4, "a": 3, "d": 0 },
    { "seq": 25, "r": 5, "c": 9, "a": 4, "d": 0 },
    { "seq": 26, "r": 6, "c": 1, "a": 7, "d": 5 },
    { "seq": 27, "r": 6, "c": 2, "a": 0, "d": 5 },
    { "seq": 28, "r": 6, "c": 3, "a": 0, "d": 5 },
    { "seq": 29, "r": 6, "c": 7, "a": 0, "d": 4 },
    { "seq": 30, "r": 6, "c": 11, "a": 5, "d": 0 },
    { "seq": 31, "r": 6, "c": 13, "a": 0, "d": 5 },
    { "seq": 32, "r": 6, "c": 14, "a": 0, "d": 5 },
    { "seq": 33, "r": 6, "c": 15, "a": 0, "d": 5 },
    { "seq": 34, "r": 7, "c": 1, "a": 4, "d": 0 },
    { "seq": 35, "r": 7, "c": 6, "a": 5, "d": 0 },
    { "seq": 36, "r": 7, "c": 8, "a": 0, "d": 3 },
    { "seq": 37, "r": 7, "c": 9, "a": 0, "d": 4 },
    { "seq": 38, "r": 7, "c": 10, "a": 0, "d": 9 },
    { "seq": 39, "r": 7, "c": 12, "a": 4, "d": 0 },
    { "seq": 40, "r": 8, "c": 1, "a": 4, "d": 0 },
    { "seq": 41, "r": 8, "c": 6, "a": 5, "d": 0 },
    { "seq": 42, "r": 8, "c": 12, "a": 4, "d": 0 },
    { "seq": 43, "r": 9, "c": 1, "a": 4, "d": 0 },
    { "seq": 44, "r": 9, "c": 6, "a": 5, "d": 0 },
    { "seq": 45, "r": 9, "c": 12, "a": 4, "d": 0 },
    { "seq": 46, "r": 10, "c": 1, "a": 5, "d": 0 },
    { "seq": 47, "r": 10, "c": 5, "a": 0, "d": 3 },
    { "seq": 48, "r": 10, "c": 9, "a": 7, "d": 0 },
    { "seq": 49, "r": 10, "c": 11, "a": 0, "d": 3 },
    { "seq": 50, "r": 11, "c": 4, "a": 4, "d": 0 },
    { "seq": 51, "r": 11, "c": 6, "a": 0, "d": 5 },
    { "seq": 52, "r": 11, "c": 7, "a": 0, "d": 5 },
    { "seq": 53, "r": 11, "c": 10, "a": 3, "d": 0 },
    { "seq": 54, "r": 12, "c": 3, "a": 5, "d": 4 },
    { "seq": 55, "r": 12, "c": 9, "a": 5, "d": 4 },
    { "seq": 56, "r": 12, "c": 13, "a": 0, "d": 4 },
    { "seq": 57, "r": 13, "c": 1, "a": 4, "d": 3 },
    { "seq": 58, "r": 13, "c": 2, "a": 0, "d": 3 },
    { "seq": 59, "r": 13, "c": 6, "a": 5, "d": 0 },
    { "seq": 60, "r": 13, "c": 8, "a": 0, "d": 3 },
    { "seq": 61, "r": 13, "c": 12, "a": 4, "d": 0 },
    { "seq": 62, "r": 13, "c": 14, "a": 0, "d": 3 },
    { "seq": 63, "r": 13, "c": 15, "a": 0, "d": 3 },
    { "seq": 64, "r": 14, "c": 1, "a": 4, "d": 0 },
    { "seq": 65, "r": 14, "c": 6, "a": 5, "d": 0 },
    { "seq": 66, "r": 14, "c": 12, "a": 4, "d": 0 },
    { "seq": 67, "r": 15, "c": 1, "a": 4, "d": 0 },
    { "seq": 68, "r": 15, "c": 6, "a": 5, "d": 0 },
    { "seq": 69, "r": 15, "c": 13, "a": 3, "d": 0 }
  ]
}

"""
        grid = Grid.from_json(jsonstr)
        return grid
Example #15
0
 def test_grid_from_puzzle(self):
     puzzle = TestPuzzle.create_nyt_daily()
     jsonstr = puzzle.to_json()
     grid = Grid.from_json(jsonstr)
     self.assertEqual(puzzle.n, grid.n)
Example #16
0
    def get_good_grid():
        jsonstr = """
        {
  "n": 9,
  "cells": [
    "+-----------------+",
    "|*| | | |*| | | | |",
    "|*| | | |*| | | | |",
    "| | | | |*| | | | |",
    "| | | | | |*| | | |",
    "| | | | | | | | | |",
    "| | | |*| | | | | |",
    "| | | | |*| | | | |",
    "| | | | |*| | | |*|",
    "| | | | |*| | | |*|",
    "+-----------------+"
  ],
  "black_cells": [
    [ 1, 1 ],
    [ 1, 5 ],
    [ 2, 1 ],
    [ 2, 5 ],
    [ 3, 5 ],
    [ 4, 6 ],
    [ 6, 4 ],
    [ 7, 5 ],
    [ 8, 5 ],
    [ 8, 9 ],
    [ 9, 5 ],
    [ 9, 9 ]
  ],
  "numbered_cells": [
    { "seq": 1, "r": 1, "c": 2, "a": 3, "d": 9 },
    { "seq": 2, "r": 1, "c": 3, "a": 0, "d": 9 },
    { "seq": 3, "r": 1, "c": 4, "a": 0, "d": 5 },
    { "seq": 4, "r": 1, "c": 6, "a": 4, "d": 3 },
    { "seq": 5, "r": 1, "c": 7, "a": 0, "d": 9 },
    { "seq": 6, "r": 1, "c": 8, "a": 0, "d": 9 },
    { "seq": 7, "r": 1, "c": 9, "a": 0, "d": 7 },
    { "seq": 8, "r": 2, "c": 2, "a": 3, "d": 0 },
    { "seq": 9, "r": 2, "c": 6, "a": 4, "d": 0 },
    { "seq": 10, "r": 3, "c": 1, "a": 4, "d": 7 },
    { "seq": 11, "r": 3, "c": 6, "a": 4, "d": 0 },
    { "seq": 12, "r": 4, "c": 1, "a": 5, "d": 0 },
    { "seq": 13, "r": 4, "c": 5, "a": 0, "d": 3 },
    { "seq": 14, "r": 4, "c": 7, "a": 3, "d": 0 },
    { "seq": 15, "r": 5, "c": 1, "a": 9, "d": 0 },
    { "seq": 16, "r": 5, "c": 6, "a": 0, "d": 5 },
    { "seq": 17, "r": 6, "c": 1, "a": 3, "d": 0 },
    { "seq": 18, "r": 6, "c": 5, "a": 5, "d": 0 },
    { "seq": 19, "r": 7, "c": 1, "a": 4, "d": 0 },
    { "seq": 20, "r": 7, "c": 4, "a": 0, "d": 3 },
    { "seq": 21, "r": 7, "c": 6, "a": 4, "d": 0 },
    { "seq": 22, "r": 8, "c": 1, "a": 4, "d": 0 },
    { "seq": 23, "r": 8, "c": 6, "a": 3, "d": 0 },
    { "seq": 24, "r": 9, "c": 1, "a": 4, "d": 0 },
    { "seq": 25, "r": 9, "c": 6, "a": 3, "d": 0 }
  ]
}
"""
        grid = Grid.from_json(jsonstr)
        return grid