Is My Sudoku Algorithm Ethical?

4/21/2025

Instead of doom scrolling, I’ve been trying to spend my downtime playing more intellectually stimulating puzzle games. Chiefly among them is Sudoku. Sudoku has a special place in my heart because it’s simple. There’s only one rule. Numbers must be one-of-a-kind in their square, row, and column. I love that I can pick up my phone anytime and enjoy it--I’m partial to the Apple News+ puzzle section. But there’s a catch. I hate, when I inevitably get stuck, not knowing how a puzzle ends.

I’m no genius, especially at Sudoku. Most games I fill the board, inevitably finding some deep mistake, and bored of the effort, give up; comforted by the few solid squares I know to be true. What bothers me is not knowing where I made my mistake. I don’t want to waste a day on a puzzle if I’ve got other stuff to do, so yesterday, around Easter family commitments, I wrote a brute force algorithm to solve standard 9x9 Sudoku puzzles.

Sudoku Solver

Today, before work, I wanted to publish my solver online to share with my friends in Discord.The only problem is that I wrote my algorithm in Go and this website is in PHP. I didn’t want to go through the hassle of building an entire service to host this small program, so I threw my entire library into Claude and asked it to write an interface that would allow players to submit puzzles and solve them using my algorithm with the click of a button. This page--sans article--is the result. Which leads to my question: is my Sudoku algorithm ethical?

I wrote the code for my original algorithm. It is copyrighted to me and does not use any third-party libraries besides github.com/charmbracelet/log for logging and the Go standard library. Claude rewrote my algorithm in pure JavaScript, changing nothing material about how the algorithm worked besides translating it into a different programming language. It did write the interface and a new validation method that just makes sure the initial puzzle input is valid, but it did not fundamentally change my algorithm.

View the code 👇

type Cell struct {
	Value uint
	Fixed bool
}

func (c Cell) Empty() bool {
	return c.Value == 0
}

// Row, Col
type Board [][]Cell

// The above code was pulled from another file for context

func (b Board) BruteForce() bool {

	for row := range b {
		for col := range b[row] {
			if b[row][col].Empty() {
				// fmt.Println(row+1, col+1)
				for i := uint(1); i <= 9; i++ {
					if !b.RowHas(row, i) && !b.ColHas(col, i) && !b.BoxHas(row, col, i) {
						if err := b.Set(row, col, i); err != nil {
							log.Fatalf("%q: row %d, col %d, %d", err, row, col, i)
						}

						if b.BruteForce() {
							return true
						}

						b[row][col] = Cell{}

					}
				}

				return false
			}

		}
	}

	return true
}

func (b Board) BoxHas(row, col int, value uint) bool {
	startRow := (row / 3) * 3
	startCol := (col / 3) * 3

	for r := range 3 {
		for c := range 3 {
			if b[startRow+r][startCol+c].Value == value {
				return true
			}
		}
	}

	return false
}


// RowHas returns true if the value exists in the row.
func (b Board) RowHas(row int, value uint) bool {
	for _, cell := range b[row] {
		if cell.Value == value {
			return true
		}
	}
	return false
}

// ColHas returns true if the value exists in the column.
func (b Board) ColHas(col int, value uint) bool {
	for row := range b {
		if b[row][col].Value == value {
			return true
		}
	}
	return false
}

                
Versus Claude's code:

// Cell class
class Cell {
    constructor(value = 0, fixed = false) {
        this.value = value;
        this.fixed = fixed;
    }
    isEmpty() {
        return this.value === 0;
    }
}
// Board class
class Board {
    constructor(rows = 9, cols = 9) {
        this.cells = Array(rows).fill().map(() =>
            Array(cols).fill().map(() => new Cell())
        );
    }
    // Set a value in the board
    set(row, col, val) {
        if (this.cells[row][col].fixed) {
            return false;
        }
        this.cells[row][col].value = val;
        return true;
    }
    // Check if a value exists in a row
    rowHas(row, value) {
        return this.cells[row].some(cell => cell.value === value);
    }
    // Check if a value exists in a column
    colHas(col, value) {
        return this.cells.some(row => row[col].value === value);
    }
    // Check if a value exists in a 3x3 box
    boxHas(row, col, value) {
        const startRow = Math.floor(row / 3) * 3;
        const startCol = Math.floor(col / 3) * 3;
        for (let r = 0; r < 3; r++) {
            for (let c = 0; c < 3; c++) {
                if (this.cells[startRow + r][startCol + c].value === value) {
                    return true;
                }
            }
        }
        return false;
    }
    // Brute force solver
    bruteForce() {
        for (let row = 0; row < 9; row++) {
            for (let col = 0; col < 9; col++) {
                if (this.cells[row][col].isEmpty()) {
                    for (let i = 1; i <= 9; i++) {
                        if (!this.rowHas(row, i) && !this.colHas(col, i) && !this.boxHas(row, col, i)) {
                            this.cells[row][col].value = i;
                            if (this.bruteForce()) {
                                return true;
                            }
                            this.cells[row][col].value = 0;
                        }
                    }
                    return false;
                }
            }
        }
        return true;
    }
    // Load the board with fixed values
    loadBoard(values) {
        for (let r = 0; r < 9; r++) {
            for (let c = 0; c < 9; c++) {
                const val = values[r][c];
                if (val !== 0) {
                    this.cells[r][c] = new Cell(val, true);
                } else {
                    this.cells[r][c] = new Cell();
                }
            }
        }
    }
    // Get the current state as a 2D array
    getValues() {
        return this.cells.map(row =>
            row.map(cell => cell.value)
        );
    }
    // Check if the board is valid
    isValid() {
        // Check rows
        for (let row = 0; row < 9; row++) {
            const seen = new Set();
            for (let col = 0; col < 9; col++) {
                const val = this.cells[row][col].value;
                if (val !== 0) {
                    if (seen.has(val)) return false;
                    seen.add(val);
                }
            }
        }
        // Check columns
        for (let col = 0; col < 9; col++) {
            const seen = new Set();
            for (let row = 0; row < 9; row++) {
                const val = this.cells[row][col].value;
                if (val !== 0) {
                    if (seen.has(val)) return false;
                    seen.add(val);
                }
            }
        }
        // Check boxes
        for (let boxRow = 0; boxRow < 3; boxRow++) {
            for (let boxCol = 0; boxCol < 3; boxCol++) {
                const seen = new Set();
                for (let row = 0; row < 3; row++) {
                    for (let col = 0; col < 3; col++) {
                        const r = boxRow * 3 + row;
                        const c = boxCol * 3 + col;
                        const val = this.cells[r][c].value;
                        if (val !== 0) {
                            if (seen.has(val)) return false;
                            seen.add(val);
                        }
                    }
                }
            }
        }
        return true;
    }
}
        

Who owns this code? It’s my algorithm, but Claude did all the work of translating it. The UI certainly was of its own creation, and I wouldn’t consider it mine, but the algorithm itself is basically as I would have written it. I think I contributed the meat and potatoes to the meal, but Claude brought the Palatschinke. AI art cannot receive copyrights, but art predominantly made by a human, even if they used an AI tool can. So, do I own the Claude code?

By my musings, the code is mine. Although the code itself was not written by me, it was directly instructed to translate code that I had written to another language. In this case, Claude is a contractor I’ve hired to make changes based off of something I own. I’ve licensed my algorithm to Claude to translate to a new language, but the contractor used no creativity to complete its job. Since the algorithm, and its material implementation did not change, the code to actually solve the puzzle belongs to me. As for the interface. Imagine I had not written this article, and had only the original artifact hosted. The UI code would be in the public domain and unable to receive copyright. I didn’t add anything material to the design. I served as a product manager in this relationship, asking the designer to create something and make tweaks. Of the two of us, it's obvious the designer would have the copyright. So, in this case, this page should be dual licensed. The game code is mine, even though Claude, acting as a contractor updated it, and the frontend is public domain, because although I asked for tweaks, the designer did all the work.

This is just a taste of the headache that working with AI generated materials is becoming. What does it mean to own our work when we have no hard answers about who owns what in what we do? We may get a legal answer in New York Times vs OpenAI that will reshape the definition of Fair Use as we know it. That’s why, right now, it’s more important than ever to decide for yourself where the line gets drawn within the limitations of existing copyright law.