Hello's recent activity

  1. Comment on Day 17: Chronospatial Computer in ~comp.advent_of_code

    Hello
    Link
    Part 1 was straightforward conversion of requirements to code. For part 2, based on inspection of the code (in my input, have no idea how well this generalizes), I saw that the same prefix in the...

    Part 1 was straightforward conversion of requirements to code.
    For part 2, based on inspection of the code (in my input, have no idea how well this generalizes), I saw that the same prefix in the octal representation in register A always corresponded to the same suffix in the output, so I built up the output by adding to the prefix of register A digit-by-digit and seeing which next digit(s) correspond to the desired output.

    Part 1 (Python)
    with open('input/17.txt', 'r') as f:
        lines = f.read().splitlines()
    
    register_a = int(lines[0].split()[-1])
    register_b = int(lines[1].split()[-1])
    register_c = int(lines[2].split()[-1])
    
    program = list(map(int, lines[4].split()[1].split(',')))
    
    program_pointer = 0
    program_output = []
    
    def combo_operand(operand: int):
        if operand <= 3:
            return operand
        if operand == 4:
            return register_a
        if operand == 5:
            return register_b
        if operand == 6:
            return register_c
        raise ValueError(f'Invalid combo operand {operand}')
    
    def adv(operand: int):
        global register_a
        res = register_a // (2 ** combo_operand(operand))
        register_a = res
    
    def bxl(operand: int):
        global register_b
        register_b = register_b ^ operand
    
    def bst(operand: int):
        global register_b
        register_b = combo_operand(operand) % 8
    
    def jnz(operand: int):
        global program_pointer
        if register_a != 0:
            program_pointer = operand - 2
    
    def bxc(operand: int):
        global register_b
        register_b = register_b ^ register_c
    
    def out(operand: int):
        program_output.append(combo_operand(operand) % 8)
    
    def bdv(operand: int):
        global register_b
        res = register_a // (2 ** combo_operand(operand))
        register_b = res
    
    def cdv(operand: int):
        global register_c
        res = register_a // (2 ** combo_operand(operand))
        register_c = res
    
    operations = [adv, bxl, bst, jnz, bxc, out, bdv, cdv]
    
    while 0 <= program_pointer < len(program):
        operation = program[program_pointer]
        operand = program[program_pointer + 1]
        operations[operation](operand)
        program_pointer += 2
    
    print("Part 1:", ",".join(map(str,program_output)))
    
    Part 2 (Python)
    # 2,4,1,3,7,5,4,2,0,3,1,5,5,5,3,0
    
    # WHILE A != 0:
        # bst 4: B = A % 8
        # bxl 3: B = B ^ 3
        # csv 5: C = A // 2 ** B
        # bxc: B = B ^ C
        # adv 3: A = A // 8
        # bxl 5: B = B ^ 5
        # out 5: B % 8
    
    target_output = "".join(map(str, program))
    
    def run(a: int):
        b = 0
        c = 0
        output = []
        while a != 0:
            b = a % 8
            b = b ^ 3
            c = a // (2 ** b)
            b = b ^ c
            a = a // 8
            b = b ^ 5
            output.append(b % 8)
        return "".join(map(str, output))
    
    encountered_prefixes = set()
    prefix_queue = [""]
    length = len(target_output)
    lowest_match = None
    while len(prefix_queue) > 0:
        prefix = prefix_queue.pop(0)
        if prefix in encountered_prefixes:
            continue
        encountered_prefixes.add(prefix)
        #print(prefix, len(matches))
        for i in range(8):
            s = prefix + str(i) + "0" * (length - len(prefix) - 1)
            a = int(s, 8)
            result = run(a)
            if len(result) < length:
                continue
            if result == target_output:
                if lowest_match is None or a < lowest_match:
                    lowest_match = a
                    print("Part 2:", lowest_match)
                    exit()
            idx = length - len(prefix) - 1
            if target_output[idx] == result[idx]:
                prefix_queue.append(prefix + str(i))
    
    2 votes
  2. Comment on Day 15: Warehouse Woes in ~comp.advent_of_code

    Hello
    Link
    There isn't anything particularly interesting to say about this one, since it was essentially just an exercise in translating the mechanics of the puzzle into code, and didn't require any clever...

    There isn't anything particularly interesting to say about this one, since it was essentially just an exercise in translating the mechanics of the puzzle into code, and didn't require any clever tricks or optimizations. It took me about 40 minutes of fiddling around to get the cascading pushing to work correctly in part 2.

    Part 1 (Python)
    with open('input/15.txt', 'r') as f:
        grid_text, moves = f.read().split("\n\n")
    
    grid = {}
    for y, row in enumerate(grid_text.split("\n")):
        for x, cell in enumerate(row):
            location = x + y * 1j
            grid[location] = cell
            if cell == "@":
                roboot_location = location
    
    movemap = {
        ">": 1,
        "<": -1,
        "^": -1j,
        "v": 1j
    }
    
    def perform_move(grid, roboot_location, move):
        new_location = roboot_location + movemap[move]
        boxes_to_push = []
        while grid[new_location] == "O":
            new_location = new_location + movemap[move]
            boxes_to_push.append(new_location)
    
        if grid[new_location] == "#":
            return roboot_location
        if grid[new_location] == ".":
            for box in boxes_to_push:
                grid[box] = "O"
            grid[roboot_location] = "."
            roboot_location = roboot_location + movemap[move]
            grid[roboot_location] = "@"
            return roboot_location
    
    def get_coord(position):
        return int(position.imag) * 100 + int(position.real)
    
    def print_grid(grid, grid_text):
        for y in range(grid_text.count("\n") + 1):
            for x in range(len(grid_text.split("\n")[0])):
                print(grid.get(x + y * 1j, " "), end="")
            print()
    
    for move in "".join(moves.split()):
        roboot_location = perform_move(grid, roboot_location, move)
    
    box_coord_sum = sum([get_coord(position) for position, cell in grid.items() if cell == "O"])
    print("Part 1:", box_coord_sum)
    
    Part 2 (Python)
    widened_grid_text = grid_text.replace("#","##").replace("O", "[]").replace(".", "..").replace("@", "@.")
    grid = {}
    for y, row in enumerate(widened_grid_text.split("\n")):
        for x, cell in enumerate(row):
            location = x + y * 1j
            grid[location] = cell
            if cell == "@":
                roboot_location = location
    
    
    def perform_move(grid, roboot_location, move):
        if grid[roboot_location + move] == "#":
            return roboot_location
        
        locations_to_push_from = [roboot_location]
        boxes_to_push = []
    
        while any([grid[location + move] in "[]" for location in locations_to_push_from]):
            if any([grid[location + move] == "#" for location in locations_to_push_from]):
                return roboot_location
            new_locations_to_push_from = []
            for location in locations_to_push_from:
                boxes = []
                if grid[location + move] == "[":
                    boxes = [location + move]
                    if move in [1j, -1j]:
                        boxes.append(location + move + 1)
                elif grid[location + move] == "]":
                    boxes = [location + move]
                    if move in [1j, -1j]:
                        boxes.append(location + move - 1)
                boxes_to_push.extend(boxes)
                new_locations_to_push_from.extend(boxes)
            locations_to_push_from = new_locations_to_push_from
    
        if any([grid[box+move] == "#" for box in boxes_to_push]):
            return roboot_location
    
        new_symbols = {}
        for box in boxes_to_push:
            symbol = grid[box]
            new_symbols[box + move] = symbol
        for box in boxes_to_push:
            grid[box] = "."
        for box, symbol in new_symbols.items():
            grid[box] = symbol
    
        grid[roboot_location] = "."
        roboot_location = roboot_location + move
        grid[roboot_location] = "@"
        return roboot_location
    
    for move in "".join(moves.split()):
        roboot_location = perform_move(grid, roboot_location, movemap[move])
    
    box_coord_sum = sum([get_coord(position) for position, cell in grid.items() if cell == "["])
    print("Part 2:", box_coord_sum)
    
    1 vote
  3. Comment on Day 14: Restroom Redoubt in ~comp.advent_of_code

    Hello
    Link
    Part 1 was straightforward calculation of (initial_position + velocity * time) % grid_size For part 2 I kept track of how many horizontally adjacent robots there were at each time, figuring that...

    Part 1 was straightforward calculation of (initial_position + velocity * time) % grid_size

    For part 2 I kept track of how many horizontally adjacent robots there were at each time, figuring that the configuration with the Christmas tree would have the most of these. Fortunately that was correct. In a weak attempt to try to make it possible to work on anyone's input, I output the first time where there are at least 200 robots adjacent to each other in a horizontal direction.

    Part 1 (Python)
    from collections import Counter
    
    with open('input/14.txt', 'r') as f:
        lines = f.read().splitlines()
    
    robots = []
    for line in lines:
        position, velocity = line.split()
        position = position.split('=')[1].split(',')
        velocity = velocity.split('=')[1].split(',')
        px, py = int(position[0]), int(position[1])
        vx, vy = int(velocity[0]), int(velocity[1])
        robots.append((px, py, vx, vy))
    
    width = 101
    height = 103
    
    def get_position_after_time(robot, t):
        px, py, vx, vy = robot
        return ((px + vx * t) % width, (py + vy * t) % height)
    
    def get_quadrant(position):
        px, py = position
        if px == width // 2 or py == height // 2:
            return None
        return (px < width // 2, py < height // 2)
    
    quadrants = Counter()
    for robot in robots:
        new_position = get_position_after_time(robot, 100)
        quadrant = get_quadrant(new_position)
        if quadrant is not None:
            quadrants[quadrant] += 1
    
    safety_factor = 1
    for v in quadrants.values():
        safety_factor *= v
    
    print("Part 1:", safety_factor)
    
    Part 2 (Python)
    def count_horizontal_adjacencies(positions):
        adjacencies = 0
        pos_set = set(positions)
        for y in range(height):
            for x in range(width - 1):
                if (x, y) in pos_set and (x + 1, y) in pos_set:
                    adjacencies += 1
        return adjacencies
    
    for t in range(10000):
        positions = [get_position_after_time(robot, t) for robot in robots]
        adjacencies = count_horizontal_adjacencies(positions)
        if adjacencies > 200:
            print("Part 2:", t)
            break
    
    3 votes
  4. Comment on Day 13: Claw Contraption in ~comp.advent_of_code

    Hello
    Link
    It's probably a bit like cutting a loaf of bread with a chainsaw, but z3 makes this problem trivial. Part 1+2 import re from z3 import * with open('input/13.txt', 'r') as f: input = f.read()...

    It's probably a bit like cutting a loaf of bread with a chainsaw, but z3 makes this problem trivial.

    Part 1+2
    import re
    from z3 import *
    
    with open('input/13.txt', 'r') as f:
        input = f.read()
    
    machines = [m.split("\n") for m in input.split('\n\n')]
    for i, (a, b, prize) in enumerate(machines):
        ax, ay = re.search(r'Button A: X\+(\d+), Y\+(\d+)', a).groups()
        bx, by = re.search(r'Button B: X\+(\d+), Y\+(\d+)', b).groups()
        px, py = re.search(r'Prize: X=(\d+), Y=(\d+)', prize).groups()
        machines[i] = (int(ax), int(ay), int(bx), int(by), int(px), int(py))
    
    def solve(machine, offset=0):
        ax, ay, bx, by, px, py = machine
        a = Int('a')
        b = Int('b')
        o = Optimize()
        o.add(a >= 0, b >= 0)
        o.add(a * ax + b * bx == px + offset)
        o.add(a * ay + b * by == py + offset)
        o.minimize(3 * a + b)
        if o.check() == sat:
            return o.model().eval(3 * a + b).as_long()
        return 0
    
    part_1 = 0
    part_2 = 0
    for machine in machines:
        part_1 += solve(machine)
        part_2 += solve(machine, 10000000000000)
    
    print("Part 1:", part_1)
    print("Part 2:", part_2)
    
    2 votes
  5. Comment on Day 12: Garden Groups in ~comp.advent_of_code

    Hello
    Link
    Part 1 was just a flood fill to get sets of coordinates for separate regions. Area is just the number of coordinates in each region, and perimeter is taking each coordinate in the region, checking...

    Part 1 was just a flood fill to get sets of coordinates for separate regions. Area is just the number of coordinates in each region, and perimeter is taking each coordinate in the region, checking the four directions around it, and adding 1 for each adjacent coordinate not in the region.

    Part 2 borrowed the perimeter method from part 1, but collapsed adjacent coordinate/direction pairs together into equivalence classes.

    Part 1
    with open('input/12.txt', 'r') as f:
        lines = f.read().splitlines()
    
    grid = {}
    for y, line in enumerate(lines):
        for x, c in enumerate(line):
            grid[x + y * 1j] = c
    
    def flood_fill(grid, start):
        region = set([start])
        symbol = grid[start]
        queue = [start]
        while queue:
            pos = queue.pop()
            for d in [1, -1, 1j, -1j]:
                new_pos = pos + d
                if new_pos in grid and new_pos not in region and grid[new_pos] == symbol:
                    region.add(new_pos)
                    queue.append(new_pos)
        return region
    
    
    regions = []
    
    uncovered = set(grid.keys())
    while len(uncovered) > 0:
        start = uncovered.pop()
        region = flood_fill(grid, start)
        uncovered -= region
        regions.append((grid[start], region))
    
    def get_area(region):
        return len(region[1])
    
    def get_perimeter(region):
        perimeter = 0
        for pos in region[1]:
            for d in [1, -1, 1j, -1j]:
                new_pos = pos + d
                if new_pos not in region[1]:
                    perimeter += 1
        return perimeter
    
    price = 0
    for region in regions:
        area, perimeter = get_area(region), get_perimeter(region)
        price += area * perimeter
    
    print("Part 1:", price)
    
    Part 2
    def get_sides_count(region):
        perimeter_objects = set()
        for pos in region[1]:
            for d in [1, -1, 1j, -1j]:
                new_pos = pos + d
                if new_pos not in region[1]:
                    perimeter_objects.add((new_pos, d))
        
        distinct_sides = 0
        while len(perimeter_objects) > 0:
            pos, d = perimeter_objects.pop()
            distinct_sides += 1
            next = pos + d * 1j
            while (next, d) in perimeter_objects:
                perimeter_objects.remove((next, d))
                next += d * 1j
            next = pos + d * -1j
            while (next, d) in perimeter_objects:
                perimeter_objects.remove((next, d))
                next += d * -1j
        return distinct_sides
    
    price = 0
    for region in regions:
        area, sides = get_area(region), get_sides_count(region)
        price += area * sides
        
    print("Part 2:", price)
    
    4 votes
  6. Comment on Day 11: Plutonian Pebbles in ~comp.advent_of_code

    Hello
    Link
    The main insight for me when solving part 2 was that the list contains many stones of the same number, so I can just keep track of the counts of the unique stones. Part 1 & 2 from collections...

    The main insight for me when solving part 2 was that the list contains many stones of the same number, so I can just keep track of the counts of the unique stones.

    Part 1 & 2
    from collections import Counter
    
    with open('input/11.txt', 'r') as f:
        stones = list(map(int, f.readline().split()))
    
    stone_count = Counter(stones)
    
    def transform_stone(n):
        s = str(n)
        if n == 0:
            yield 1
        elif len(s) % 2 == 0:
            half = len(s) // 2
            yield int(s[:half])
            yield int(s[half:])
        else:
            yield n * 2024
    
    def blink(stone_count : Counter):
        new_stones = Counter()
        for s, count in stone_count.items():
            for t in transform_stone(s):
                new_stones[t] += count
        return new_stones
    
    s = stone_count
    for i in range(75):
        s = blink(s)
        if i + 1 == 25:
            print("Part 1:", s.total())
        if i + 1 == 75:
            print("Part 2:", s.total())
    
    6 votes
  7. Comment on Day 10: Hoof It in ~comp.advent_of_code

    Hello
    Link
    I was satisfied that for part 2 all I had to do was comment out one line of my code from part 1 that was checking to see if I had already encountered a location in the grid. I actually...

    I was satisfied that for part 2 all I had to do was comment out one line of my code from part 1 that was checking to see if I had already encountered a location in the grid. I actually accidentally got the output of part 2 in my first iteration of solving part 1.

    Part 1 & 2
    with open('input/10.txt', 'r') as f:
        lines = f.read().splitlines()
    
    grid = {}
    for y, line in enumerate(lines):
        for x, char in enumerate(line):
            grid[x + y * 1j] = int(char)
    
    trailheads = [key for key, value in grid.items() if value == 0]
    
    def get_score(trailhead, count_distinct_paths=False):
        score = 0
        queue = [trailhead]
        encountered_positions = set()
        while len(queue) > 0:
            position = queue.pop(0)
            if position in encountered_positions:
                continue
            if not count_distinct_paths:
                encountered_positions.add(position)
            elevation = grid[position]
            if elevation == 9:
                score += 1
                continue
            surrounding_positions = [position + direction for direction in [1, -1, 1j, -1j] if position + direction in grid and grid[position + direction] == elevation + 1]
            queue.extend(surrounding_positions)
        return score
    
    
    print("Part 1:", sum(get_score(trailhead) for trailhead in trailheads))
    print("Part 2:", sum(get_score(trailhead, count_distinct_paths=True) for trailhead in trailheads))
    
    2 votes
  8. Comment on You can now translate sign language automatically with these amazing Raspberry Pi glasses in ~tech

    Hello
    Link
    Not sure who the intended users of this would be. This apparently only work with fingerspelled letters, which is a tiny part of sign language, so it could only work if somebody communicated...

    Not sure who the intended users of this would be. This apparently only work with fingerspelled letters, which is a tiny part of sign language, so it could only work if somebody communicated entirely by fingerspelling every single word, which pretty much nobody ever does because ain't nobody got time for that. Saying that you can "translate sign language" with these glasses is about as exaggerated as saying you can "translate Chinese" if you know pinyin.

    After watching the demo video, it looks even more useless, because it looks like the hand has to be held in position rather carefully and held for a fairly long duration, which will be hopeless for understanding Deaf people who tend to fingerspell very rapidly and use a lot of shortcuts.

    13 votes
  9. Comment on Why it’s time to stop worrying about the decline of the English language in ~humanities.languages

    Hello
    Link Parent
    Do you have any examples? I have some familiarity with sign language linguistics and am unaware of any sign language in widespread use today that could be classified as a conlang.

    Do you have any examples? I have some familiarity with sign language linguistics and am unaware of any sign language in widespread use today that could be classified as a conlang.

  10. Comment on Dad jokes - I'm in need of something fresh in ~life.men

    Hello
    Link Parent
    I don't understand the second one. Is it important to know some fact about George Michael to get the joke? I've never heard of him before.

    I don't understand the second one. Is it important to know some fact about George Michael to get the joke? I've never heard of him before.

    3 votes
  11. Comment on Russian President Vladimir Putin says prosecution of Donald Trump shows US political system is 'rotten' in ~society

    Hello
    Link
    Got it, so prosecuting a political rival is rotten, but poisoning them with Novichok is just fine.

    Got it, so prosecuting a political rival is rotten, but poisoning them with Novichok is just fine.

    11 votes
  12. Comment on <deleted topic> in ~life.women

    Hello
    Link Parent
    I agree with the overall thrust of your post, but I'm just curious about the legal ramifications if the father is also under the age of consent. If two 13 year olds have sex with each other, and...

    I agree with the overall thrust of your post, but I'm just curious about the legal ramifications if the father is also under the age of consent. If two 13 year olds have sex with each other, and neither is able to consent, does that mean that they were both raped?

    1 vote
  13. Comment on Grand jury in the US state of Georgia returns indictments in Donald Trump 2020 election case in ~society

    Hello
    Link Parent
    Rules are usually created in reaction to a situation, and since this is a fairly unprecedented situation in the US it's not surprising that rules haven't caught up with it yet. That being said, I...

    Rules are usually created in reaction to a situation, and since this is a fairly unprecedented situation in the US it's not surprising that rules haven't caught up with it yet.

    That being said, I think disqualification just based on being indicted (not criminally convicted) would run afoul of the principles of due process and innocent-until-proven-guilty.

    10 votes
  14. Comment on The actual danger from AI is mostly not what is getting talked about in ~tech

    Hello
    Link
    So the article says that misalignment and reinforcing bias are minor problems that are not in the same league as COVID-19 and nuclear weapons. But then the problem that is in the same league as...

    So the article says that misalignment and reinforcing bias are minor problems that are not in the same league as COVID-19 and nuclear weapons. But then the problem that is in the same league as those is that people will become too lazy to use their judgement skills?

    To me, that seems like a problem in the same league as every other invention that has made people "lazier". The invention of the printing press made people too lazy to memorize stories that had previously been passed down orally. The invention of the calculator made people too lazy to do calculations mentally or on a piece of paper. The invention of the car made people too lazy to walk. The invention of lawn mowers made people too lazy to develop their scythe skills.

    3 votes
  15. Comment on Without saying where you live, where do you live? in ~talk

    Hello
    Link Parent
    The US isn't the only place in the world, could also be the Sahara.

    The US isn't the only place in the world, could also be the Sahara.

    3 votes
  16. Comment on Without saying where you live, where do you live? in ~talk

    Hello
    Link Parent
    I've known some BBQ snobs who say that if the meat is cooked properly then it doesn't need any sauce, and the purpose of sauce is just to mask the flavor of poorly-cooked meat. (I'm not one of...

    It's the sauce that makes a good BBQ

    I've known some BBQ snobs who say that if the meat is cooked properly then it doesn't need any sauce, and the purpose of sauce is just to mask the flavor of poorly-cooked meat.

    (I'm not one of them, but it goes to show how controversial BBQ opinions are)

    2 votes
  17. Comment on Megathread for news/updates/discussion of ChatGPT and other AI chatbots in ~tech

    Hello
    Link Parent
    To be fair, it's probably a human-complete task too. If everyone around you is convinced the president died of a spontaneous combustion, then it's likely you will be too.

    To be fair, it's probably a human-complete task too. If everyone around you is convinced the president died of a spontaneous combustion, then it's likely you will be too.

    3 votes
  18. Comment on I'm working on an app for learning Chinese, anyone interested in helping me test it? in ~tech

    Hello
    Link Parent
    I think I've fixed this now, but if you see it again let me know. Thanks again!

    I think I've fixed this now, but if you see it again let me know. Thanks again!

    1 vote
  19. Comment on I'm working on an app for learning Chinese, anyone interested in helping me test it? in ~tech

    Hello
    Link Parent
    Hey, thanks for the suggestions! Good point. I'll consider adding a raw APK link to the home page at some point, but in the meantime you can try this:...

    Hey, thanks for the suggestions!

    Though, I do have an Android device, I do not have access to the Google play store. It would be nice if there was just an APK download.

    Good point. I'll consider adding a raw APK link to the home page at some point, but in the meantime you can try this:

    http://data.dong-chinese.com/apk/dong-chinese.apk

    I just uploaded it now, so I haven't actually verified yet whether or not it will function correctly. I think everything should work normally, except that I'm pretty sure that the log-in with Google function will not work since this APK file isn't signed with the Google play key.

    That way the second 是 can't be copied from the revealed 是 if the first one is skipped.

    If the user writes the first 是 incorrectly, it will be already be marked as incorrect for that sentence and they won't get any credit for writing the second one correctly. Given that, I don't see any harm in letting them copy from the revealed character.

    As I find I learn better when I have to type it, and it's more fulfilling.

    That's something I'll consider, the only thing I'm worried about is people who don't have a Chinese keyboard installed and don't know how to input characters in a text field. By default I'll probably leave it as it is now, but maybe I'll add an option in the settings to enable something like that.

    Are there any plans for a listening section?

    I have thought about it a number of times, but there are a few details that make me unsure of how to design it properly.

    • How to make a listening exercise where you can't "cheat" by reading. If I have a fill-in-the-blank exercise with visible characters, the user can just read them without having to listen. The only thing I've thought of is multiple-choice where you have to listen to each option, but I suspect that it would be rather tedious for the user.
    • How will listening progress interact with reading/writing progress. Right now, if you only write characters, it will also increase your reading scores because you can probably read a character if you can write it. The reverse is not true - doing reading exercises will not increase writing scores. I'm not sure whether or not listening should be treated as a completely separate skill.

    Thanks again for the feedback, I really appreciate it!

    1 vote
  20. Comment on I'm working on an app for learning Chinese, anyone interested in helping me test it? in ~tech

    Hello
    Link Parent
    Hi, thanks for the comments. Just a few things I'm having trouble reproducing or confused about: I just tried taking a test and canceled it midway. When I started it again it started afresh from...

    Hi, thanks for the comments. Just a few things I'm having trouble reproducing or confused about:

    Cancelling the test midway and the restarting doesn't seem possible.

    I just tried taking a test and canceled it midway. When I started it again it started afresh from the beginning.
    Were you expecting it to start in the middle from where you canceled it before?

    Would prefer entering a full sentence instead of one word at a time, which I find really slow

    I'm not sure I envision what this would look like. You would write the characters small enough to write the whole sentence in the box?

    Might be nice to be able to skip a single word and not the entire writing question.

    Are you talking about the test? If you tap skip during the writing test it only skips one character (and marks it as wrong).

    Suggestion - maybe add ability for someone to write a quick explanation or fix sentence when sending feedback if they choose

    In the feedback form there's an "other reason" text field where you can write anything. Did you have something else in mind?

    2 votes