[HN Gopher] Arthur Whitney's one liner sudoku solver (2011)
___________________________________________________________________
Arthur Whitney's one liner sudoku solver (2011)
Author : secwang
Score : 264 points
Date : 2024-10-06 00:00 UTC (23 hours ago)
(HTM) web link (dfns.dyalog.com)
(TXT) w3m dump (dfns.dyalog.com)
| wileydragonfly wrote:
| Sudoku was always a meditative thing for me. It's impossible not
| to win so long as you pay attention. Optimizing solutions seems
| contrary to the point to me.
| UncleOxidant wrote:
| I guess I'm the opposite. After doing a couple of sudoku many
| years ago my thought was "Hey, I could just automate this" and
| started thinking of algorithms.
| swatcoder wrote:
| Solvers are useful for confirming that a puzzle you've recieved
| or generated is solvable. The meditative process can really go
| sideways when there is no solution for you to stumble upon.
|
| Puzzles in commercial collections don't _usually_ have that
| problem, but those from other sources sometimes do.
|
| Solvers also make for a nice craft exercise, as here. Simple
| but not trivial, you can approach them in a lot of different
| ways and thereby work through different techniques or
| constraints you mean to explore.
| sellyme wrote:
| > Puzzles in commercial collections don't _usually_ have that
| problem,
|
| I would argue that puzzles in commercial collections are more
| likely to have that problem than ones made freely available
| by hobbyists, as commercial enterprises inevitably cut
| corners on things like labour costs for an actual human
| setter.
|
| I have seen dozens of commercial puzzle games and
| applications that do not make any attempt to verify the
| (auto-generated) puzzles as solvable, but I don't think I've
| ever had the same problem on a site like LMD.
| malux85 wrote:
| Optimising solutions is the meditative exercise for me.
|
| I enjoy running simulation after simulation after simulation,
| studying possible outcomes and optimising everything. Everyone
| is different :)
| gerdesj wrote:
| Meta: No need to DV a comment you don't like for no reason.
| Engage instead. Why not have a chat?
| riiii wrote:
| People are saturated with anger and frustration after doom
| scrolling. They engage with their pitchforks.
| mcphage wrote:
| A few anonymous downvotes are what qualifies as pitchforks
| these days?
| swatcoder wrote:
| Downvotes and upvotes work together to manage the visibility
| of posts that align with the community's tastes.
|
| While I myself found an opportunity to reply to the GP and
| didn't down vote them, their comment only engaged with the
| article in a shallow way and only then, seemingly, to just
| dismiss the concept of solver altogether.
|
| It wasn't a offensive comment, but it didn't really
| contribute to the site in the way many people digging into
| deep technical walkthroughs like this expect to see.
|
| Some downvotes weren't guaranteed, but they're not surprising
| and they're probably helping new readers stay engaged with
| more topical and technical alternatives.
|
| It's not the end of the world to get a few downvotes, and
| it's almost never personal. It certainly isn't here.
| jksmith wrote:
| Aside: Downvotes on HN can be an expression of age related,
| self-righteous sniper pique; Opinions on what contributes
| to a conversation can be all over the place and are
| entirely subject to biases, which can be interesting (I
| guess). Doesn't really matter, and Hail Satan anyway. Also
| "Q for Mortals" is an interesting book.
| ben0x539 wrote:
| Wouldn't it be more productive/rewarding to instead engage
| with comments I do like?
| otteromkram wrote:
| Only you can say what's best for you.
|
| If have to ask: What's rewarding about only having your
| viewpoint reinforced?
| ben0x539 wrote:
| Where are you getting the viewpoints thing from?
| johnisgood wrote:
| Just under this submission I have upvoted a handful of
| comments with which I disagreed, mainly because of its
| replies.
| K0balt wrote:
| I find that sodoku is not a math or even a logic puzzle, but
| rather an epistemology puzzle. Lots of how we know/how much we
| know, and if you get into speed with some failure tolerance
| through estimating probability it adds even more thought
| provoking rabbit holes.
| kranner wrote:
| Optimising a Sudoku solver can be seen as a different puzzle
| entirely and not as a mode of playing Sudoku.
| teo_zero wrote:
| Interesting position that was not expressed before. However
| please note that the same could be said about writing a solver.
| 29athrowaway wrote:
| There is a video about this.
|
| https://www.youtube.com/watch?v=DmT80OseAGs
|
| You can try the solution at https://tryapl.org/
| nebulous1 wrote:
| Here is the line, it is written in K. K is a language created by
| the same person (Arthur Whitney) based on APL and Scheme.
| x(,/{@[x;y;]'(!10)^x*|/p[;y]=p,:,3/:-3!p:!9 9}')/&~*x
| Isamu wrote:
| Not knowing K, am I correct in assuming this is a backtracking
| brute force solver?
| o11c wrote:
| From the linked page (and the one linked beyond that), it's a
| breadth-first search actually. Keep a list of possible puzzle
| states at all times, pick a blank cell (theoretically
| arbitrary, but in practice intelligently for performance), add
| copies of the state with each possibility for that state added.
| BobbyTables2 wrote:
| That sounds like 100+ lines in python or similar languages...
| otteromkram wrote:
| It probably isn't. At least, not for Python.
| throwup238 wrote:
| You should be able to do it in under 20 lines using the
| same matrix operations as the K code via numpy.
| anonzzzies wrote:
| Numpy is indeed very apl. Just more horrible to me; not
| python-y and annoyingly verbose for the apl-er.
| hrzn wrote:
| A few years back I made a modest attempt at writing a
| concise yet readable sudoku solver in Python - in about 29
| lines: https://github.com/hrzn/sudoku/blob/master/sudoku.py
|
| Could have been made shorter at the price of readability.
| forgotpwd16 wrote:
| Looks nice. Since imports numpy can utilize (more of)
| numpy's operations to squeeze validation functions and
| nested fors to one. Should result in shorter code but
| readability will probably depend on reader's experience
| in array programming.
| dzaima wrote:
| The k code at least isn't doing any heuristics for the
| iteration order, and is just doing a fold over the indices of
| zeroes in index-ascending order.
| shahbazac wrote:
| I've often wondered about languages like APL/k, are the
| programmers actually able to think about problems more
| efficiently?
| giraffe_lady wrote:
| Hillel Wayne writes about it on his newsletter every once in a
| while. He's convinced me that he does in fact think through
| some problems better in array languages but I still can't
| really conceive of what that experience is like.
| RodgerTheGreat wrote:
| there are several open-source K environments available, some
| which even run in the browser:
|
| http://johnearnest.github.io/ok/index.html
|
| if it's something you're interested in trying i'd be happy to
| point you toward more resources, and i'm sure there are
| plenty of other arraylang tinkerers reading this thread who
| could help, too
| Jorge1o1 wrote:
| As a kdb+/Q programmer I would say it depends on the type of
| problem.
|
| For example, when working with arrays of data it certainly is
| easier to think and write "avg a+b" to add two arrays together
| and then take the average.
|
| In a non-array programming language you would probably first
| need to do some bounds checking, then a big for loop, a
| temporary variable to hold the sum and the count as you loop
| over the two arrays, etc.
|
| Probably the difference between like 6ish lines of code in some
| language like C versus the 6 characters above in Q.
|
| But every language has features that help you reason about
| certain types of problems better. Functional languages with
| algebraic data types and pattern matching (think OCaml or F#)
| are nicer than switch statements or big if-else-if statements.
| Languages with built-in syntactic sugar like async/await are
| better at dealing with concurrency, etc.
| lll-o-lll wrote:
| Which is why C# is the giant ever increasing bag of tricks
| that it is (unkind people might say bloat...) ;-) Personally,
| I'm all for this; let me express the problem in whatever way
| is most natural.
|
| There are limits, of course, and it's not without downsides.
| Still, if I have to code in something all day, I'd like that
| "something" be as expressive as possible.
| sudosysgen wrote:
| Well no, not in a non-array programming language. In any
| language that has a semi-decent type/object system and some
| kind of functional programming support, `avg a+b` would just
| be `avg(a, b)`, which is not any easier or harder, with an
| array type defined somewhere. Once you make your basic array
| operations (Which they have to be made in q anyways, just in
| the stdlib), you can compose them just like you would in q,
| and get the same results. All of the bounds checking and for-
| loops is unnecessary, all you really need are a few HKTs that
| do fancy maps and reduces, which the most popular languages
| already have.
|
| A very real example of this is Julia. Julia is not really an
| array-oriented programming language, it's a general language
| with a strong type system and decent functional programming
| facilities, with some syntactic sugar that makes it look like
| it's a bit array oriented. You could write any Q/k program in
| Julia with the same complexity and it would not be any more
| complex. For a decently complex program Julia will be faster,
| and in every case it will be easier to modify and read and
| not any harder to write.
| rak1507 wrote:
| I don't know what you mean by the q array operations being
| defined in the standard library. Yes there are things
| defined in .q, but they're normally thin wrappers over k
| which has array operations built in.
| sudosysgen wrote:
| I don't consider an interpreted language having
| operations "built-in" be significantly different from a
| compiled language having basic array operations in the
| stdlib or calling a compiled language.
| rak1507 wrote:
| Hmm, why not? Using K or a similar array language is a
| very different experience to using an array library like
| numpy.
| otteromkram wrote:
| Why would it be avg(a, b)?
|
| What if I want to take the average difference of two
| arrays?
| IshKebab wrote:
| mean(a - b)
| omoikane wrote:
| I went to this tech talk on Dyalog (a modern APL-like
| language), and the speaker makes the argument that the notation
| allows certain idioms to be recognized more easily:
|
| https://youtu.be/PlM9BXfu7UY?si=ORtwI1qmfmzhJGZX&t=3598
|
| This particular snippet was in the context of compilers, but
| the rest of the talk has more on Dyalog and APL as a system of
| mathematical notation. The underlying theme is that optimizing
| mathematical expressions may be easier than optimizing general
| code.
| jksflkjl3jk3 wrote:
| For some classes of problems that are easily vectorized, using
| an array-focused language can certainly make thinking about
| them and their solutions more efficient, since you can abstract
| over the data structure and iteration details.
|
| As a quant, I used kdb+/q quite a bit for 5+ years for mid-
| frequency strategies, but as I moved towards higher frequency
| trading that required calculations on the order book that
| couldn't be easily or efficiently vectorized, then continuing
| to use array-focused languages would have only complicated
| reasoning about those problems.
| upghost wrote:
| What did you switch to after that?
| 082349872349872 wrote:
| one nice thing about the array language style is that it's
| possible to talk about variations on algorithms where the
| relevant code snippets, being a few characters, fits inline
| into the discussion; more traditional vertically-oriented
| languages that take handfuls or dozens of lines to say the same
| things need to intersperse code display blocks with expository
| prose
| lokedhs wrote:
| "More efficiently"? Maybe. It opens up a new way to think about
| solutions to problems. Sometimes those solutions are more
| efficient, and sometimes they are just different.
|
| It's a useful thing to learn though. And dare I say it, fun.
| Even if there was zero benefit to it, it'd still be fun. As it
| turns out, there really are benefits.
|
| For me, the biggest benefit is when I'm working with data
| interactively. The syntax allows me to do a lot of complex
| operations on sets of data with only a few characters, which
| makes you feel like you have a superpower (especially when
| comparing to someone using Excel to try to do the same thing).
| michaelg7x wrote:
| I've found that the challenge is to "think in vector
| operations" rather than of iterating over the same data. The
| tricky part is figuring out how to get an operator to do the
| right thing over an array of stuff on the left hand side and
| this list/bag/etc of arguments on the right
| pjot wrote:
| > Advocates of the language emphasize its speed, facility in
| handling arrays, and expressive syntax.
|
| Indeed.
|
| https://en.m.wikipedia.org/wiki/K_(programming_language)
| brookst wrote:
| "Expressive" = like two cats fought while standing on the
| keyboard
| xwolfi wrote:
| I work with it daily in a bank, and I couldnt find a better
| way to express it. Many colleagues throwing their keyboard in
| despair at this stupid impossible to remember syntax.
| rak1507 wrote:
| There are a lot of things in various programming languages
| which are hard to remember, but k and array languages have
| such a small surface area, not being able to remember it
| while working with it daily amounts to learned
| helplessness.
|
| (source: mostly amateur k programmer, also worked with it
| in a bank, find it vastly easier to read/write/remember
| than most mainstream languages)
| aguaviva wrote:
| Which is why banks love it. Because one has to be pretty
| smart to be able to wade through all that nutty, impossible
| to remember syntax.
|
| Therefore, k is a smart choice, and people who use k in
| business applications must be really smart.
| inopinatus wrote:
| "Debugging is twice as hard as writing a program in the
| first place. So if you're as clever as you can be when
| you write it, how will you ever debug it?"
|
| -- Kernighan, Brian. _The Elements of Programming Style
| (2e)_. McGraw-Hill, 1978.
| userbinator wrote:
| By becoming even more clever:
|
| https://www.linusakesson.net/programming/kernighans-
| lever/in...
| IshKebab wrote:
| Very unconvincing. If you become cleverer you can just
| write even _more_ clever code and still not be able to
| debug it.
| chmod775 wrote:
| Great!
| nine_k wrote:
| It's a nice interpretation.
|
| I prefer a different approach: "smart is good; clever
| isn't smart". If you have to express something in a
| clever, that is, highly contrived but actually working
| way, it means that you lack the right way to express it,
| and maybe your mental model of the _problem_ is not that
| good. The theory of epicycles is clever; Kepler 's laws
| are smart.
| darkwater wrote:
| This can probably true for some people, but still will
| not work for many other. One probable outcome of a
| frustrated debugging session is "let's rewrite/refactor
| it to make it easier to debug next time", and not self-
| enlightenment.
| chii wrote:
| > how will you ever debug it?
|
| By being so smart that your program has obviously zero
| bugs in it!
| nosianu wrote:
| This view is too static.
|
| That is not possible, because the environment can (and at
| some point always will) change which wasn't planned for
| due to a lack of working crystal balls. Data, user
| behavior, the network, the system(s) the software runs on
| can all change over time.
|
| Also, it is way too expensive to try to cover every
| single conceivable possibility, so we deliberately leave
| holes.
|
| For non-trivial things we often prefer to wait to see
| what problems actually come up during use, and then fix
| exactly those, but not the many more problems that
| _could_ come up but are too unlikely and /or too costly
| to guard against.
|
| In a living environment the software lives too, and keeps
| changing and adapting.
| chii wrote:
| you might've missed the quip, since this whole thread is
| about a quote, which i'm countering with an alternative
| quote from Hoare
|
| > There are two methods in software design. One is to
| make the program so simple, there are obviously no
| errors. The other is to make it so complicated, there are
| no obvious errors.
| boomlinde wrote:
| _> That is not possible, because the environment can (and
| at some point always will) change which wasn 't planned
| for due to a lack of working crystal balls. Data, user
| behavior, the network, the system(s) the software runs on
| can all change over time._
|
| It sounds to me like you are describing a change of
| problem, not bugs in the solution. If in the future
| someone redefines the concept of a Sudoku puzzle such
| that this solution is no longer applicable, or tries to
| use the solution verbatim in a language which is
| different from K and therefore yields different results,
| it's not a bug in the original code that it's not a
| solution to that new problem. It's still a solution to
| the same problem it was always a solution to.
|
| I can see what you mean in a practical sense, but also
| consider (practically) that a lot of problems can be
| broken down into smaller, well-defined problems which are
| themselves practically immutable. You can throw the
| solutions to such problems out when you no longer need
| them, and come up with solutions to whatever new problems
| replaced the original problems.
| RHSeeger wrote:
| In my experience, the vast majority of problems are
| insufficiently specified. No matter how well you solve
| the current problem, there are bound to be certain
| assumptions you've made about the requirements. And when
| those assumptions don't hold true, your solution may no
| longer work.
|
| > What do you mean the input file can't be ISO-2WTF
| encoded?
| dotancohen wrote:
| I believe that you are addressing maintainability, not
| debugging.
| ryanjshaw wrote:
| The average bank/company would rather have an average
| solution maintained by 10 easily replaceable average
| developers than a nutty, smart solution only understood
| by 1 highly talented developer.
| Moru wrote:
| You could also say that the average bank/company should
| have learned from previous mistakes doing exactly that
| for many decades. Select a language that is well tested,
| understood and supported. Set a limit on cleverness and
| instead focus on maintainability and simplicity.
| speed_spread wrote:
| If only. In my experience, banks end up building a
| solution that is maintained by 100 mediocre developers
| that a reasonably smart developer can't make sense of
| when it behaves erratically or has extremely poor
| performance.
| ryanjshaw wrote:
| I described the theory. You have described the practice
| :)
| aguaviva wrote:
| Which was precisely my point (and I agree with all the
| responses in this thread), though my wording and light
| sarcasm seems to have been a bit too dry, and didn't
| quite take up as intended.
| queuebert wrote:
| Keeping skill barriers low keeps wages low as well.
| nine_k wrote:
| Not that it's impossible to remember, bit it's definitely
| contrary to most traditional use of the symbols employed in
| it, though not without logic. My favorite is the functions
| from io package, called 0, 1, and 2 (yes, numbers) which
| handle interaction with stdin, stdout, and stderr
| respectively. In dyadic form they at least have a colon,
| but in monadic form they look like plain numbers: 1 "Hello
| world".
|
| I suspect that to study k (and use kdb) efficiently, you
| need to actively forget what you knew about the syntax of
| other languages, and study k as a language from Mars that
| happens to map to ASCII characters somehow.
| anonzzzies wrote:
| It is really easy to remember; it is so small that
| remembering is the least of the issue. The rest is just
| using it a lot; I find it readable and nice to work with.
| Unlike other some other languages we get shoved down your
| throats.
| rtpg wrote:
| I've been messing with Uiua (https://www.uiua.org/) a good
| amount recently, and find its sort of dance between having a
| stack and being an array language somehow gets you to a nice
| level of legibility despite being a combo of two styles that
| tend to generate line noise.
| g8oz wrote:
| Thanks for the link, it looks like a fascinating language
| dahart wrote:
| The front page there has examples like
| "/3/+[?][?]x[?]x1.5.220xt/[?]|&asr" - is that closer to
| noise, or does it actually look more readable than K once
| you get used to both? I'm kind-of intrigued by the built-in
| multimedia output, but still this language looks scary and
| impractical at first glance. How does it compare to using
| numpy & jupyter? Do a lot of people prefer the extreme
| tenseness over using typeable keywords? I'm curious why it
| lets you type the readable operators but wants to turn them
| into glyphs; wouldn't it be more approachable, more
| readable, and make more maintainable code, if it just used
| the keywords instead of glyphs?
| rtpg wrote:
| Well firstly you can just type keywords and they get
| swapped by glyphs
|
| Secondly, the power with Uiua is you can easily split up
| that expression literally by just hitting enter in some
| spots (and line swapping but...). You can give names
|
| And finally if you are constantly doing operations across
| arrays it can be more legible to go a symbol like
| approach, like how most people prefer x+y to add x y. The
| conciseness helps you build up larger expressions before
| you need to break things up.
| PhilipRoman wrote:
| Cool language. I happened to notice the [?] operator, which
| operates on a transformed array, then reverts the
| transformation. Not sure if other array languages include
| this, but it's a really cool idea. I always found the
| traditional map/filter operators to be limiting in this
| regard, kind of like trying to write expressions without
| using parentheses.
| mlochbaum wrote:
| It's in several, particularly newer APL dialects; see
| https://aplwiki.com/wiki/Under#History . Proud to say I
| originated the "structural" form used by Uiua, which is
| able to deal with transformations like filtering that
| lose parts of the input. Every language now seems to have
| its own take on what exactly Under means, with different
| implementations leading to different supported functions
| and various ways to relax the theory to be more
| convenient or handle intuitively expected things better.
| rtpg wrote:
| Under is a concept in array languages, though it's
| supported in a very adhoc way
| hilux wrote:
| But possibly not its maintainability.
| cduzz wrote:
| I'll sometimes gauge code complexity by comparing the number of
| lines of code against the output of tar -cf - . |
| gzip | base64 | wc -l
|
| IE "how much does it compress?"
|
| Looking at APL -- I'm reminded of what happens if I accidentally
| send the gzipped output to my tty...
|
| I'm impressed that there's anyone who can follow along (can you
| find the bug?) to code like
|
| p-{(|[?])[?]{([?][?].=[?])/[?]nxn[?]}",[?]},(n*/2){[?],[?][?][?][
| ?]/[?]}'[?]n n-[?][?]
|
| It really feels like compressed binary data where everyone's got
| a copy of the dictionary already...
| rak1507 wrote:
| I'm not sure why it would be any more impressive or surprising
| than the billions of people who read and write in non English
| alphabets
| cduzz wrote:
| That's a really good point...
|
| But -- (and forgive me if I'm totally wrong) -- this isn't
| just "non-english" but "non-phonetic" which is a smaller set
| of written languages, and the underlying language is ...
| math.... so understanding the underlying grammer itself
| relies on having decades of math education to really make it
| jive.
|
| If this code is just a final result of "learn math for 2-3
| decades, and spend years learning this specific programming
| language" -- my statement stands. Interacting with this kinda
| binary blob as a programming language is impressive. I think
| I read somewhere that seymour cray's wife knew he was working
| too hard when he started balancing the checkbook in hex...
| rak1507 wrote:
| The underlying language isn't really very mathematical, at
| most there's a bit of linear algebra in the primitives but
| that's it. You certainly don't need any sort of formal
| maths education to learn APL. There are about 50 or so new
| symbols, which is not a big ask, with any sort of focus the
| majority of the syntax etc can be learned very quickly. The
| "bugs" in your original code stand out very clearly because
| things like "[?]}" don't make sense, [?] being "dyadic"
| (infix).
| RodgerTheGreat wrote:
| and it bears mention that a decent chunk of those symbols
| are things nearly everyone is familiar with from other
| languages (+, -, =, etc), symbols you've probably seen in
| math class or on your graphing calculators (/, x, [?],
| [?], -, etc), and symbols with very strong mnemonic
| associations once you've seen them explained ([?], [?],
| [?], [?], etc).
| bryancoxwell wrote:
| Legitimately curious how APL programmers think about
| maintainability and readability. Is code just thoroughly
| commented or otherwise documented?
| shawn_w wrote:
| I suspect that if you're fluent in the language,
| understanding an expression written in it comes just as
| easily and quickly as reading a sentence in a book does to
| me.
| andylynch wrote:
| That's exactly what they say. Though most kdb I've see in
| business looks more like Python.
| campbel wrote:
| Information density is studied in linguistics. It could
| likely apply to programming languages similarly.
| genewitch wrote:
| i've only seen these style of languages commented _after_ a
| contest is over on stack programming challenges. I have no
| idea how one would learn all this stuff from code in the wild
| (like i learned most of python, for example). then again, i
| don 't go searching github for k, apl, or _perl_ for that
| matter.
|
| I'm sure each of those languages makes some guarantee about
| the sorts of errors that can be introduced - as opposed to C
| (let me pick on it) where the errors you know you can
| introduce, and the errors that are introduced aren't a large
| union. However i have a hard enough time typing english
| consistently, so the various "symbol-y" languages just glaze
| my eyes, unfortunately.
|
| It almost "feels" like these languages are an overreaction to
| the chestnut "they must get paid by LoC".
| t-3 wrote:
| You don't really have to worry about keeping track of tons of
| functions, variables, structs, classes, etc., and trying to
| keep all the names straight in your head - all you need is to
| know the symbols, so it's in some ways easier than reading a
| complex function in more verbose languages where you might
| need to lookup stuff from several libraries just to
| understand what's going on. Also, that one line is ~100
| characters, each of which probably covers ~0.5-1 lines in
| other languages, so you should expect to set aside a similar
| amount of time to reading and understanding it.
| rtpg wrote:
| my impression is that the language is used more for scripts
| than for "code" in a true sense. A bit of "how much can you
| juggle in your mind" going on
| dzaima wrote:
| Once you've learned the syntax of the language, long
| expressions like that are about as readable as however-many-
| dozen lines of JS/Python with 1-to-3-character variable
| names; i.e. some parts may be obvious if they're a common
| pattern or simple enough, but the big picture may take a
| while to dig out.
|
| Probably the biggest readability concern of overly-golfed
| expressions really is just being dynamically typed, a problem
| shared with all dynamically-typed languages. But array
| languages have the problem worse, as nearly all operations
| are polymorphic over array vs number inputs, whereas in e.g.
| JS you can use 'a+b' as a hint that 'a' and 'b' are numbers,
| & similar.
|
| If you want readable/maintainable code, adding comments and
| splitting things into many smaller lines is just as
| acceptable as in other languages.
| upghost wrote:
| I am kind of curious if you have to mentally keep track of
| the rank/shape/dimensions in your head or if there is some
| implicit/explicit convention for conveying that to the
| reader. Does tracking rank/shape become second nature after
| awhile?
|
| I'm also wondering about things like (APL-style) inner
| products -- they are undeniably powerful, but it's hard for
| me to conceptual use cases above rank 3.
| lokedhs wrote:
| That depends on the specific code. Some code is written
| to be agnostic to the rank, while others make certain
| assumptions.
|
| In my code I'd sometimes write assertions in the
| beginning of a function to not only ensure it's called
| with the right shape but also as documentation.
|
| Also, in practice really high rank arrays aren't used
| much. Even 4 is pretty rare.
| dzaima wrote:
| If there's information on input format, it is simple
| enough to trace through the following shapes, but it does
| force reading the code rather linearly. Operations which
| implicitly restrict the allowed shapes are unfortunately
| intentionally rather few.
|
| I basically never use the generalized inner product; it's
| rather unique to the original APL - J has a variant that
| doesn't have the built-in reduction, and k and BQN and
| many if not most other array languages don't have any
| builtin for it at all. And in general I don't typically
| use rank higher than like one plus the natural
| dimensionality of the operation/data in question.
| realo wrote:
| I programmed in APL a long time ago... even got 'not bad'
| at it.
|
| The best analogy i can give of my thought process is that
| first i unfolded the problem into one or more many-
| dimension object(s) ... then took a different "stance" of
| looking at the object, then refolded them into the final
| solution.
|
| So yes... I had it all in my head at some point.
| mlochbaum wrote:
| The most uncompromisingly APL-ish code I've written is the
| BQN compiler[0]. Hard to write, hard to extend, hard to
| refactor. I generally recommend against writing this way in
| [1]. But... it's noticeably easy to debug. There's no control
| flow, I mean, with very few exceptions every line is just run
| once, in order. So when the output is wrong I skim the
| comments and/or work backwards through the code to find which
| variable was computed wrong, print stuff (possibly comparing
| to similar input without the bug) to see how it differs from
| expectations, and at that point can easily see how it got
| that way.
|
| The compiler's whole state is a bunch of integer vectors, and
| *Show [a,b,c] prints some equal-length vectors as rows of a
| table, so I usually use that. The relevant code is usually a
| few consecutive lines, and the code is composed of very basic
| operations like boolean logic, reordering arrays with
| selection, prefix sum, and so on, so they're not hard to read
| if you're used to them. There are a few tricks, which almost
| all are repeated patterns (e.g. PN, "partitioned-none" is
| common enough to be defined as a function). And fortunately,
| the line prefaced with "Permutation to reverse each
| expression: _more_ complicated than it looks " has never
| needed to be debugged.
|
| Basically, when you commit to writing in an array style (you
| don't have to! It might be impossible!) you're taking an
| extreme stance in favor of visible and manipulable data. It's
| more work up front to design the layout of this data and
| figure out how to process it in the way you want, but easier
| to see what's happening as a result. People (who don't know
| APL, mostly) say "write only" but I haven't experienced it.
|
| [0] https://github.com/mlochbaum/BQN/blob/master/src/c.bqn
|
| [1] https://mlochbaum.github.io/BQN/implementation/codfns.htm
| l#i...
| upghost wrote:
| God bless, my hat goes off to you sir. I have trouble
| wrapping my head around the _concept_ of first class
| functions in ndarrays, let alone implementing it in
| hardcore APL. That has to be a feat on par with Hsu 's Co-
| Dfns.
|
| Don't suppose you can point to any resources to help wrap
| your head around BQN, do you?
| mlochbaum wrote:
| Well this is pretty much the goal of the BQN website so
| my best attempts are there. I might point to the quick
| start page https://mlochbaum.github.io/BQN/doc/quick.html
| as a way to feel more comfortable with the syntax right
| away. And the community page
| https://mlochbaum.github.io/BQN/community/index.html
| collects links by others; Sylvia's blog in particular
| focuses on the sorts of flat array techniques that are
| useful for a compiler.
| upghost wrote:
| Just looked at the github -- wait, you _wrote_ BQN? My
| God. Is there any prior art on this -- arraylangs with
| first class functions? I don 't think very many people
| realize how incredible the semantic power of BQN is. The
| idea of an arraylang with first class functions... it
| truly staggers the imagination.
|
| I feel like if I were able to wrap my head around it I
| would never want to code in anything else. Thanks again
| and excited to take another look at it!
| mlochbaum wrote:
| K, for a start. Whitney's earlier dialect A+ too. See
| https://aplwiki.com/wiki/First-class_function .
| smabie wrote:
| Don't most array languages have first class functions?
| upghost wrote:
| They have functions but not _first class_ functions.
| Think (the ability to make) a vector /matrix of
| _functions_ rather than just numbers :O
|
| What could you do with that?
|
| I don't know, but I bet some pretty cool stuff.
| bear8642 wrote:
| > What could you do with that?
|
| Just a couple quick ideas: fns - f g h
| [?] array of function fns[condition] args
| [?] select function to run (3 0 0[?]fns) args
| [?] f f f args
| notfed wrote:
| What is this witchcraft? I fear that I have seen something
| that I cannot unsee...
| make3 wrote:
| "one line in your custom language" is not one line at all lol
| Spivak wrote:
| To be fair K is a real language that's used by more than just
| him.
|
| Why array languages seem to gravitate to symbol soup that makes
| regex blush I'll never know.
| IshKebab wrote:
| Yeah I think MATLAB and Mathematica are waaay more used than
| K et al. They just don't look insane so people aren't posting
| them on HN as much.
| asah wrote:
| What baud is that? /s
| genewitch wrote:
| mismatched, whatever it is, that's for sure. It's not quite
| line noise, so maybe it's just the wrong stop bit?
| speed_spread wrote:
| My cat puked in the modem receiver cup, sorry.
| dang wrote:
| I put 2011 in the title above because
| https://web.archive.org/web/20110813135700/https://dfns.dyal...
| appears to have the main thing - is there a better year?
| lofaszvanitt wrote:
| It has strong perl vibes and it brings back ptsd :D. Maybe this
| overshortification of things is a personnel or intelligence
| indicator of some sorts.
| gorgoiler wrote:
| Every K program ought to end in _QED_ , and then I remember that
| KQED is also a thing, and I wonder if their two worlds have ever
| overlapped.
|
| (KQED is the Bay Area PBS partner. PBS is the US public
| television org.)
| lucw wrote:
| Does anyone have any thoughts on what motivates people to play
| sudoku or write solvers for sudoku ? I have trouble finding
| motivation to solve artificial problems. That said I sink
| hundreds of hours into factorio.
| riffraff wrote:
| I don't particularly enjoy sudoku but I like word puzzle games.
|
| They're all artificial problems, but your brain likes a
| challenge and you get a dopamine hit when you solve it, I
| suppose.
| bramhaag wrote:
| For me personally, I have little motivation to do classical
| sudokus. They either have a not-so-elegant solve path (usually
| set by a computer) or are too difficult for me to solve.
|
| Variant sudokus on the other hand are a lot of fun. They often
| have very elegant solve paths and there are many neat tricks
| you can discover and reason about.
|
| Some fun ones, if you'd like to try:
|
| - https://logic-
| masters.de/Raetselportal/Raetsel/zeigen.php?id...
|
| - https://logic-
| masters.de/Raetselportal/Raetsel/zeigen.php?id...
|
| - https://logic-
| masters.de/Raetselportal/Raetsel/zeigen.php?id...
| akleemans wrote:
| I also mostly enjoy Sudoku variants, most of which I
| discovered via Geocaches, interestingly. After solving a few
| I then implemented a solver with customizable constraints, if
| anyone's interested, should still be available here:
|
| https://www.sudoku-solver.ch/
| sltkr wrote:
| To each their own, but the puzzles you linked seem really
| convoluted compared to regular Sudoku.
|
| The last puzzle has no fewer than 9 custom rules, in addition
| to the regular Sudoku rules, and then it also says "every
| clue is wrogn [sic]" implying there is some meta out-of-the-
| box thinking required to even understand what the rules are.
| That is more a riddle than a logic puzzle.
|
| By contrast, the charm of classical Sudoku is that the rules
| are extremely simple and straightforward (fill the grid using
| digits 1 through 9, so that each digit occurs exactly once in
| each row, column, and 3x3 box) and any difficulty solving
| comes from the configuration of the grid.
| jessekv wrote:
| Normally I would concur, but I recently fell into a klondike
| solitaire binge and the only way out was to write a solver.
| ryanjshaw wrote:
| I wasted too much time in my youth trying to min-max, and now I
| get bored as soon as I figure out, roughly, what the rules and
| mechanics look like for any game.
| thom wrote:
| Like many puzzles, there's a regular release of endorphins as
| you progress, and a lot of satisfaction in completing
| something. I enjoy puzzles just like reading a book or playing
| a game, it's another world I can step into for a bit of an
| escape, but I like to think it's decent mental exercise.
| Overall I vastly prefer cryptic crosswords where solving each
| clue genuinely brings a smile to my face, but that's more of a
| commitment of time (and for me sometimes a guarantee of
| frustration). I also like doing puzzles in the newspaper
| because me and my kids can sit together and all contribute.
| Coffee, breakfast, sat in the sun with a newspaper and a good
| pencil[1], absolute bliss if you ask me.
|
| As for solvers, it's a very elegant, well-formed problem with a
| lot of different potential solutions, many of which involve
| useful general techniques. I used to dabble clumsily in chess
| engines and honestly it's the only time I've ever ended up
| reading Knuth directly for various bit twiddling hacks, so it's
| always educational.
|
| 1: https://musgravepencil.com/products/600-news-wood-cased-
| roun...
| teo_zero wrote:
| All games are artificial problems, so your question actually
| is, what motivates people to engage in pastimes?
|
| Sudoku, crosswords, Simon Tatham's puzzles etc. are an
| excellent way to pass the time while keep training the mind.
| Sports are their equivalent for the body.
|
| Finally, writing solvers for a problem, be it real or
| artificial, for many is just another variety of puzzle to
| engage in.
| dclowd9901 wrote:
| I don't care much for sudoku but I do enjoy crosswords quite a
| lot, which feels like a somewhat arbitrary exercise. I enjoy
| the fact that I know a lot of words and it makes me feel
| clever. There's probably something to that with most puzzle
| type challenges.
| grujicd wrote:
| I play sudoku almost exclusively on the plane. It's a good way
| to lose 5-15min.
| proteal wrote:
| idk man, you ask a good question. I think the idea has to do
| with the saddle you put on the invisible horse that is the
| game's problem. Factorio has several complex saddles you must
| master to tame the beast. In factorio, you can get
| progressively better at using these saddles to tame even the
| most unwieldy scenario. Sudoku, at its heart, is not much
| different than factorio. However sudoku has one narrow problem
| with many different, increasingly nuanced ways of solving it.
| Factorio has many different "sudoku" style problems, but each
| problem needs to be handled differently, with each problem
| having increasing levels of sophistication. I think you might
| like factorio more because it's just a bigger steak to chew on,
| and you've got the right appetite.
| lgeorget wrote:
| I teach C++ and I made my students code a Sudoku solver last
| year. It's a very convenient project to give them: self-
| contained, already familiar, no OS-specific weirdness, you get
| to use STL data structures, algorithms, very gentle I/Os...
| upghost wrote:
| Most people are put off by the symbols, that wasn't really the
| issue I had.
|
| So I do love APL and arraylangs, and learning them was really
| helpful in a lot of other languages.
|
| But they never became a daily driver for me not because of the
| symbols, which were honestly fine if you stick with it long
| enough, but after about 3-4 years of dabbling on and off I hit a
| wall with APL I just couldn't get past.
|
| Most other languages I know there is a "generic-ish" approach to
| solving most problems, even if you have to cludge your way
| through suboptimally until you find "the trick" for that
| particular problem and then you can write something really
| elegant and efficient.
|
| APL it felt like there was no cludge option -- you either knew
| the trick or you didn't. There was no "graceful degredation"
| strategy I could identify.
|
| Now, is this actually the case? I can't tell if this is a case of
| "yeah, thats how it is, but if you learn enough tricks you
| develop an emergent problem solving intuition", or if its like,
| "no its tricks all the way down", or if its more like, "wait you
| didn't read the thing on THE strategy??".
|
| Orrr maybe I just don't have the neurons for it, not sure. Not
| ruling it out.
| lokedhs wrote:
| You're not wrong. It's very easy to get that impression when
| trying to learn the array languages. It's very easy for someone
| who's used these languages for a long time to look at a
| problem, and say "why did you use that really elaborate
| solution, when you can just use [?][?]-1?". No one probably
| ever told you that [?] has an inverse, and how you could use
| it.
|
| Even today, after having worked in these languages for years, I
| am still put off a bit by the walls of code that some array
| programmers produce. I fully understand the reasoning why it's
| written like that, but I just prefer a few spaces in my code.
|
| I've been working on an array language based on APL, and one of
| my original goals was to make "imperative style" programming
| more of a first-class citizen and not punish the beginner from
| using things like if-statements. It remains to be seen how well
| I succeeded, but even I tend to use a more expressive style
| when terseness doesn't matter.
|
| Here's an example of code I've written which is the part of the
| implementation that is responsible for taking any value (such
| as nested arrays) and format them nicely as text using box
| drawing characters. I want to say that this style is a middle
| ground between the hardcore pure APL style found in some
| projects and the style you'll see in most imperative languages:
| https://codeberg.org/loke/array/src/branch/master/array/stan...
| upghost wrote:
| Very nice! I like the readability-- not sure if thats just
| indicative of your style or the language, and the map
| construct is also nice. I don't remember any off-the-shelf
| map construct, at least not in Dyalog.
| lokedhs wrote:
| It's likely a combination of both. It's certainly possible
| to write Kap in a much more condensed form. But things like
| if-statements and hash maps does allow for a more
| imperative style.
| nine_k wrote:
| Lines of code is a poor metric, because languages use lines
| differently.
|
| A much better measure would be the number of nodes in a parse
| tree, of semantically meaningful non-terminals like "a constant"
| or "a function call".
|
| An even better measure would also involve the depth and the
| branching factor of that tree.
| tromp wrote:
| The preferred measure of information content is simply number
| of bits as used for instance in Algorithmic Information Theory
| [1].
|
| [1]
| https://en.wikipedia.org/wiki/Algorithmic_information_theory
| jodrellblank wrote:
| By that measure naming a variable "objUser" instead of "user"
| is better because it has more information, and naming the
| same variable "cgjkkytdvjkftujmhffetb" is even better because
| it contains more information.
|
| The parse tree approach is trying to get at a fuzzy notion of
| useful information and useful density of information.
| smokel wrote:
| The built-in functions and API to a system library spoil these
| metrics. As an example, consider HQ9+, which is pretty good at
| printing "Hello, world!" for instance.
|
| https://cliffle.com/esoterica/hq9plus/
| xelxebar wrote:
| Just... no. What are you even trying to compare? UX of a
| language matters. Clarity, thinking paradigm, expressability
| _etc._ all matter and are affected by the visual size of code.
|
| A one line solution takes up very little visual real estate.
| That matters a lot when you are working on some more complex
| problem. Flitting your eyeballs around a screen takes orders of
| magnitude less effort than scrolling around and navigating
| files. Cognitive load is important.
|
| We really need to burn this vague "only semantics matter"
| scourge that's creeped into our programmer values these days.
| I'm sorry, but I care about things like incentives against
| over-engineering, ease of directly thinking in the problem
| domain, and simplicity of the encompassing ecosystem.
|
| A terse one-line solution tells me there is virtually no room
| for over-engineering. Even without knowing K, I can see obvious
| constants side-by-side, telling me it's likely using a direct
| data representation of the problem in its code. Does K culture
| encourage code like that? Does programming in K bias you
| towards directness and simplicity? Then please, I want some of
| that special sauce on my team.
|
| </rant>
| lukan wrote:
| "A one line solution takes up very little visual real estate.
| That matters a lot when you are working on some more complex
| problem."
|
| When I work on some more complex problem, I like to think
| about the problem, not spend energy decoding condensed text.
| Scrolling a bit more verbose, but clear code, is faster for
| me.
| cenamus wrote:
| I think the difference is not a bit of scrolling, but
| rather the whole program on half a page vs 10 files a 200
| lines of mostly noise
| lukan wrote:
| There is noise and there is self explaining code. One
| liners for complex problems are a nice challenge, but are
| seldom clear to read.
| Etherlord87 wrote:
| Why not create a programming language, that uses all possible
| unicode codepoints to further decrease the number of
| characters used? That would be so much more readable!
| agumonkey wrote:
| I mentally work like what parent described. I plug ast node
| in my mind when I read. I like operating with combinators,
| graphs/trees of them that I almost naturally understand the
| results of.
|
| Any language that add complexity at that layer loses me, and
| APL, even with crude visuals is not far from that.
| dahart wrote:
| Don't get confused between using smaller keywords and
| actually understanding the problem at hand. Terse languages
| do absolutely nothing to prevent over-engineering. They might
| even contribute by giving a false sense of simplicity and a
| tendency to prevent certain kinds of code reuse. To prevent
| over-engineering on large projects, you don't need a terse
| language at all, you need the right mentality, the right
| management & product team, good team culture & cohesion,
| strong code review process, and job performance metrics that
| align with not over-producing code.
|
| It seems like parent's metric (size of parse tree) would
| easily optimize for terseness and penalize bloat, regardless
| of language, so maybe your reaction was too reflexive. UX of
| a language does matter a bit, and one that's too terse incurs
| development friction and technical debt when used in larger
| projects. Just study the history of Perl and why it's not
| widely used.
|
| What a one liner looks like is more or less the worst
| possible metric to use for large software projects. In any
| language, the style of code changes the larger the codebase,
| and cleverness and terseness become a liability.
| https://www.teamten.com/lawrence/writings/norris-
| numbers.htm...
| skrebbel wrote:
| This oneliner was obviously done for the giggles, and nobody
| pretends it's reasonably readable code. Getting anal about
| definitions here is entirely missing the point. (which is
| "look, K lets you write extremely dense code!")
| wk_end wrote:
| I don't know if that's the case, simply because _all_ code
| that I see written by array language programmers looks like
| code golf. Even the language implementation itself!
|
| https://code.jsoftware.com/wiki/Essays/Incunabulum
| mlochbaum wrote:
| Is this because all the code you see is through HN or
| similar? No one's going to share something titled "an
| unremarkable script I use to help run my business" here.
| Not sure what your threshold for code golf is, but you can
| see APL written in a variety of styles by searching Github.
| It doesn't recognize K but does have Q, which is basically
| K plus keywords, obviously promoting more verbose code.
| Whitney originated the dense style of implementation shown
| at your link, and a few other implementers (including
| myself in the past) have picked it up, but it's not that
| common. For example April, GNU APL, Kap, Goal, and Uiua all
| use an idiomatic style for their implementation languages.
|
| APL: https://github.com/search?type=code&q=language%3AAPL
|
| Q: https://github.com/search?type=code&q=language%3Aq
|
| Implementation: https://aplwiki.com/wiki/List_of_open-
| source_array_languages
| jodrellblank wrote:
| https://news.ycombinator.com/item?id=39546175
| sorokod wrote:
| The LoC count and similar metrics have the advantage of an easy
| calculation.
|
| Ultimately though,they are a proxy to a more relevant but
| difficult to determine attributes such as
|
| Given a reasonably proficient engineer, the amount of time it
| would take them to resolve a bug in code written by someone else
| or alternatively extend its functionality in some way.
| bazoom42 wrote:
| The discussions around "line noise"-languages are always
| intersting.
|
| Most programmers would agree the '/' symbol is at least as clear
| as writing 'divideBy'. The question is how often the symbols are
| used and if their frequency in code justifies learning them.
| brador wrote:
| Someone should collate exceptional human coding achievements to
| test future AI.
|
| AFAICT AI cannot replicate this, yet, will be interesting when
| that day comes.
| TZubiri wrote:
| I thought it was written by Ursula K. Le guin.
|
| Not sure where I got that from.
| Intralexical wrote:
| It may be interesting to compare this one line to "Code Golfed"
| equivalents in different programming languages:
|
| https://codegolf.stackexchange.com/questions/tagged/sudoku?t...
| forgotpwd16 wrote:
| Funnily top[1] solution for specific problem (brute-force
| Sudoku solver) is the K snippet. Second comes a J solution that
| replicates K's.
|
| [1]: https://codegolf.stackexchange.com/a/5030
| eigenvalue wrote:
| It's cool in a novelty way that it's so short, but I would
| infinitely prefer something like this for actual work and
| understanding: def solve(grid): def
| find_empty(grid): for r in range(9):
| for c in range(9): if grid[r][c] == 0:
| return r, c return None def
| is_valid(grid, num, pos): r, c = pos
| if num in grid[r]: return False
| if num in [grid[i][c] for i in range(9)]:
| return False box_r, box_c = r // 3 * 3, c // 3 * 3
| for i in range(box_r, box_r + 3): for j in
| range(box_c, box_c + 3): if grid[i][j] ==
| num: return False return
| True def backtrack(grid): empty =
| find_empty(grid) if not empty:
| return True r, c = empty for num in
| range(1, 10): if is_valid(grid, num, (r, c)):
| grid[r][c] = num if backtrack(grid):
| return True grid[r][c] = 0
| return False backtrack(grid) return grid
| upghost wrote:
| Why is this getting down-voted without comment? Comparative
| analysis is taboo, now? I don't think Arthur Whitney would feel
| the least bit threatened by some Python code.
| eigenvalue wrote:
| The K-mafia is in control. Just kidding, I don't really care
| either way...
| BoiledCabbage wrote:
| Speculation, but maybe because there is nothing of interest
| or to note in the comment.
|
| It's not clear why the poster prefers that other
| implementation, or that they understand APL or array
| programming.
|
| So as a result the comment reads as "it's in a language _I_
| don 't know. I'd prefer it in a language _I_ do know. " Which
| is a fairly useless comment.
|
| If that's not what they intended, it would be helpful for
| them to add some context to their comment.
| upghost wrote:
| Well if we are showing off sudoku solvers, it would be a sin not
| to share this one: sudoku(Rows) :-
| length(Rows, 9), maplist(same_length(Rows), Rows),
| append(Rows, Vs), Vs ins 1..9, maplist(all_distinct,
| Rows), transpose(Rows, Columns),
| maplist(all_distinct, Columns), Rows =
| [As,Bs,Cs,Ds,Es,Fs,Gs,Hs,Is], blocks(As, Bs, Cs),
| blocks(Ds, Es, Fs), blocks(Gs, Hs, Is).
| blocks([], [], []). blocks([N1,N2,N3|Ns1], [N4,N5,N6|Ns2],
| [N7,N8,N9|Ns3]) :-
| all_distinct([N1,N2,N3,N4,N5,N6,N7,N8,N9]),
| blocks(Ns1, Ns2, Ns3).
|
| While not _one_ line, to me it is pareto optimal for readable,
| elegant, and incredibly powerful thanks to the first class
| constraint solvers that ship with Scryer Prolog.
|
| If you want to learn more about it or see more of Markus's work:
|
| https://www.metalevel.at/sudoku/
|
| https://youtu.be/5KUdEZTu06o
|
| More about Scryer Prolog (a modern , performant, ISO-compliant
| prolog written mostly in rust)
|
| https://www.scryer.pl/
|
| https://github.com/mthom/scryer-prolog
| Duanemclemore wrote:
| For me one of the most important things here is the clarity of
| the problem -maker- at the top. That's the difference between the
| "Iversonian" symbolic languages (J and K included) and others. It
| doesn't have the elegance and power of a one line solution, but
| it's just so clean and comprehensible even without the
| disciplined commenting. (Although I really think lamp is not a
| good comment glyph. Sorry about the sacred cow I just took a
| swipe at fellow array nerds.)
|
| One line solutions are incredible, and tacit is mind-bendingly
| cool. To use the unique compactness of a glyph-based language as
| a way to efficiently describe and perform functional programming
| - then to do that all over arrays!? - whoever had these ideas [0]
| is utterly genius.
|
| But as someone trying to make time to write a program ground up
| in APL, knowing that I won't be able to make it just a set of
| really good one liners, that example is also significant for me.
|
| [0] https://www.jsoftware.com/papers/fork.htm
| lokedhs wrote:
| Just because you can write everything on one line without any
| spaces doesn't mean you should.
|
| You can ofcourse removethe capability to do thatand you'll
| effectively force the programmer to write more venous code, but
| then its strength as an interfacing tool is very much reduced.
|
| The Iversonian languages has the capability to write incredibly
| terse code which is really useful when working interactively.
| When you do, your code truly is write-only because it isn't
| even saved. This is the majority of code that at least I write
| in these languages.
|
| When writing code that goes in a file, you can choose which
| style you want to use, and I certainly recommend making it a
| bit less terse in those cases. The Iversonian languages are
| still going to give you organs that are much shorter than most
| other languages even even it's written in a verbose style.
| geekraver wrote:
| Much better than some of the garbage solutions I have seen,
| including from sources that should know better, like The
| Algorithm Design Handbook. Some really absurd approaches out
| there, so bad I wrote a blog post about it in 2015:
| https://www.grahamwheeler.com/post/sudoku/
___________________________________________________________________
(page generated 2024-10-06 23:02 UTC)