October 04, 2012

Jobs in 1983

Here is a remarkable tape of jobs presenting to the International Design Conference in Aspen, unearthed by Marcel Brown.

Worth a listen. It is hard to remember how long ago 1983 was. Jobs speaks about science-fiction ideas such as using computers as a new communication medium; interactive street maps based on photos of every intersection; the balance of privacy and openness in politics; the concept of an online app store; digital imagery; voice recognition; management at Apple; and his strategy for Apple's next decade.

We want to put an incredibly great computer in a book that you can carry around with you, that you can learn how to use in 20 minutes... We really want to do it with a radio link in it, so you don't need to hook up to anything and you're in communication with all these larger databases and other computers.

Remember, in 1983, PCs had no networking and no GUIs, and in his lecture, he had to explain "what is a computer program", and the nutty idea of displaying proportional fonts on computers.


Posted by David at 07:50 AM | Comments (0)

September 22, 2012

The Problem With China

Written by British expat and 16-year China resident Mark Kitto, You'll never be Chinese... why I'm leaving the country I loved is one of the most insightful, honest essays on the problems facing China I have read in a long time.

On the pent-up economic problems in China:

Everything the Party does to fix things in the short term only makes matters worse in the long term by setting off property prices again. Take the recent cut in interest rates, which was done to boost domestic consumption, which wont boost itself until the Party sorts out the healthcare system, which it hasnt the money for because it has been invested in American debt, which it cant sell without hurting the dollar, which would raise the value of the yuan and harm exports, which will shut factories and put people out of work and threaten social stability.

On the decline of community and civic culture:

Traditional family culture, thanks to 60 years of self-serving socialism followed by another 30 of the one child policy, has become a me culture. Except where there is economic benefit to be had, communities do not act together... In the small rural village where we live I am not asked about my health or that of my family, I am asked how much money our small business is making, how much our car cost, our dog.

On the implications of Chinese racism and exceptionalism:

Leadership requires empathy, an ability to put yourself in your subordinates shoes. It also requires decisiveness and a willingness to accept responsibility. Believing themselves to be unique, the Chinese find it almost impossible to empathise. Controlled by people with conflicting interests, Chinas government struggles to be decisive in domestic issues, let alone foreign ones....

On the education:

The domestic Chinese lower education system does not educate. It is a test centre. The curriculum is designed to teach children how to pass them.... Schools do not produce well-rounded, sociable, self-reliant young people with inquiring minds. They produce winners and losers. Winners go on to college or university to take business studies. Losers go back to the farm or the local factory their parents were hoping they could escape.

Kitto also points out that the biggest property bubble on the planet (in China) seems to be caused by the unreliability of Chinese banks and stock markets, and the inability of Chinese to invest overseas due to the restrictions on yuan exchange, leaving property as the only place to park savings. My wife points out that Chinese have always liked to buy land; at any rate, Chinese property is a bubble of gigantic proportions and the government does not seem to have any way to deflate it.

Kitto seems to share the view that, without more inspired leadership, a terrible cliff is on the horizon for China.

Worth a read.

(Also, here is a contrary opinion by an American expat.)


Posted by David at 02:40 PM | Comments (0)

December 10, 2011

Omega Improved

The natural method to multiply two n n matrices takes O(n3) steps, but in 1969, Volker Strassen stunned the computational world by publishing an algorithm that does it in only O(n2.808) steps. The greek letter omega is used to denote the minimum exponent for the complexity of matrix multiplication. Strassen had shown the world that omega, which had always been assumed to be 3, was actually less than 2.808.

What is the true value of omega?

Strassen's discovery touched off a flurry of research. In the following two decades, bounds on omega were improved and tuned until 1987, when Don Coppersmith and Shmuel Winograd reduced the exponent to 2.3755. That low exponent has stood as the best upper-bound for omega for 24 years.

Although the Strassen and C-W algorithms are not practical for most real-world problems, the theoretical work shows that matrix multiplication is fundamentally simpler than we would naively assume. It hints that the mathematical world has a gap in knowledge with respect to the fundamental operation of composing linear operations. Some believe (or hope) that matrix multiplication will ultimately be shown to be O(n2). But for decades nobody has been able to get the exponent lower than 2.3755 - until now.

The Race for a Better Omega is On Again

A few days ago, Virginia Vassilevska Williams published a paper achieving an exponent lower than 2.3727, improving the bound by 0.0028 and importantly showing that C-W is not the end of the story. She credits Andrew Stothers for doing thesis work last year that pointed the way to breaking the C-W record. Then she describes an algorithm that beats upon the Stothers algorithm bound further, and she describes a general method to generate further algorithms that have low exponents.

Why did it take so long to make this next improvement? In her words, "with each new tensor power, the number of new values that need to be analyzed grows quadratically. For the eighth tensor power for instance, 30 separate analyses are required! Prior to our work, each of these analyses would require a separate application of the CW techniques. It would have required an enormous amount of patience to analyze larger tensor powers, and since the third tensor power does not give any improvement, the prospects looked bleak."

Her paper (here) is an example of a breed of modern mathematics that has grown to be comfortable with using computers as a tool. Not only does she drive computer algebra systems to crack formulas with a higher level of complexity than would be practical to do when working by hand: she describes algorithms for creating algorithms, driving computers to sift through large numbers of permutations, hunting for the best ideas.

Links. The Paper; RJ Lipton's blog entry; New Scientist.


Posted by David at 07:08 AM | Comments (1)

November 06, 2011

Made In America Again

Manufacturing wages in China have risen fourfold in the last 10 years. Factory real estate in coastal Chinese cities is now several times more expensive than it is in the Southern United States. Energy costs in China are up. And on top of all this, the yuan continues its inevitable rise against the dollar: the artificially low yuan cannot last forever because Chinese foreign trade reserves cannot grow without bound.

All of these trends point in the same direction. Within the next decade - before my children are in the workforce - the cost advantages of outsourcing U.S. manufacturing to China will have evaporated, and the U.S. economy will begin to look very different again. The change will happen quickly.

A report from BCG summarizing this thinking has been making the rounds and is worth a read.

My opinion: it is a good time to be an American exporter and a bad time to be an importer. Good time to be a manufacturer and a bad time to be a retailer. If you can make a profit exporting goods to China today, then you are going to be rolling in profits in the next decade.

A list of top U.S. exporters by container volume is here. This list is dominated by high-volume low-value exports (trash and paper). Missing are companies like Boeing or Intel that do not use a lot of container space. What would be more interesting would be a good listing of exporters by dollar value.


Posted by David at 06:38 AM | Comments (3)

October 16, 2011

Avoiding Selectors for Beginners

I am finding that the jquery syntax of $('#id') is getting in the way of teaching middle-schoolers.

There are two problems: first, the generality of jQuery selectors is an unnecessary concept to know before you even know what a function is. And second, the punctuation is too much for inexperienced typers to type: shift-4 for dollar, shift-9 and shift-0 for smooth parentheses, a choice to decide between single and double quotes, and don't forget shift-3 for the hashmark.

So I am considering giving kids a template that starts all their programs with a call to $.setupids() where idvars is a function defined as follows:

$.setupids = function setupids(prefix) {
  prefix = prefix || '';
  $('[id]').each(function(j, item) {
    window[prefix + item.id] = $('#' + item.id);
  });
};

The idea is that after $.setupids();, every HTML element that has an id of "r" has a corresponding global variable "r = $('#r');" which is the jquery selecting that element (not just the bare element).

<script src="/img/spacer.gif"> 
	
	
	
Posted by David at 09:33 AM | Comments (0)

October 15, 2011

Turtle Graphics Fern with jQuery

Here is another classic turtle graphics example:

<b id="t">t</b>

Click here to see a demo.


Posted by David at 10:03 AM | Comments (1)

October 10, 2011

Learning To Program with jQuery

What is the best programming language to learn first? The right answer in today's world is Javascript - with jQuery.

Javascript is the most widely supported programming language in the world. It has readable algebraic syntax and simple typing that is forgiving for beginners. With its good support for closures, objects, and literals, it is an elegant little language with room to grow.

The main disadvantage of Javascript is its odd and unreliable GUI-oriented standard library, the HTML DOM. But jQuery has fixed that. With its efficient, robust design, jQuery has far outstripped all its rivals as the "version 2" DOM. Today, jQuery is approaching the universality of Javascript itself.

But is jQuery accessible to beginners?

This Fall I will be testing that question. Together with several Google friends, I'm teaching a learn-to-program course to a group of 17 sixth graders (we're doing it as part of the Citizen Schools program where every child in the Dorchester public middle school is required to participate in an after-school program).

The theme of the class is "web game programming" - a popular topic. We are using Javascript and jQuery on Chromebooks along with a small set of jQuery extensions including jquery-turtle, described below. We have ten hours to get the kids from "hello world" to creating their own games.

In our first two hours, we've explored the Javascript debugger and made our own webpages. Hour three is coming up. It will be our first exercise in making a simple game with some scaffolding.

Here is the example for class 3.


Posted by David at 10:30 PM | Comments (1)

October 08, 2011

jQuery-turtle

You have three img elements with ids r, x, and a. What does the following javascript program do with them?

function tick() { if ($('#r').touches('#a')) { $('#x').turn().moveto('#a').attr('src', '/public/poof.gif'); $('#a').turn().move(); } else { $('#r').turnto('#a', 10).move(10); $(window).moveto('#r'); } } setInterval(tick, 50);

Click here to see a demo.

Open-Source Library

The jQuery extension being used here makes simple javascript game dynamics accessible enough for kids to use directly. The extension is called jQuery-turtle and I am licensing it under MIT license terms. It builds on top of Zachary Johnson's useful rotation extensions to jQuery.

jQuery-turtle source code here.

The extension adds the following methods to jQuery:

$(elt).turn(degrees)      // turns elements right by a number of degrees 
$(elt).turnto(direction)  // sets absolute compass direction
$(elt).move(pixels)       // moves elements forward by a number of pixels
$(elt).moveto(location)   // sets absolute position of the center of the elements
$(elt).mirror(flip)       // mirrors elements across the current axis
$(elt).center()           // returns the absolute center coordinates of an element
$(elt).direction()        // returns the absolute compass direction of an element
$(elt).touches(target)    // true if one element touches another
$(elt).encloses(target)   // true if one element geometrically encloses another
$(elt).pen(color)         // starts tracing a line with the given style
$(elt).fill(color)        // fills the elements with the given style

Any element can be turned and walked around the screen using turtle geometry and hit-tested against any other element. Key features of this extension avoid learning barriers for beginning programmers:

  • No need to understand cartesian coordinates. Trigonometry of turtle graphics is handled.
  • Plays nicely with css: turtle heading is css rotation, and position is set using css attributes.
  • Mouse events are easy: move to the location of a mouse event by $(elt).moveto(event).
  • Hit testing is easy: pixel-precise rotated-rectangle hit testing is implemented by $(elt).touches(otherelt).
  • Objects are positioned by their center, which simplifies most game calculations.
  • Fractional positions are remembered, avoiding roundoff problems at small steps or angles.
  • Turning towards another object is implemented by $(elt).turnto(otherelt), an alternative to $(elt).turnto(degrees).
  • Window scrolling is also easy and is done by $(window).moveto(pos).
  • Every element can have a pen with its own color and size.
  • Canvas for pen lines created on-demand and does not interfere with the document.
Continue reading "jQuery-turtle"
Posted by David at 04:22 PM | Comments (0)

September 09, 2011

Python Templating with @stringfunction

I've posted a new version of the templet.stringfunction utility I have discussed before. I have been using it to make a bunch of web UI in programming projects with my son (actually pretty sophisticated little projects - js.dabbler.org). This version reflects what's needed for a 2011-era web app.

What's New

This version is still svelte, clocking in at 91 lines of python code (plus comments and tests). Version 3.3 changes the following from v2:

  • The old class-template idioms in the original version have been removed; they were slow and unwieldy. Now it's just @stringfunction and @unicodefunction.
  • Common uses of $ in javascript no longer need to be escaped in templates. Jquery $. and $( are very common, and regular expressions often have $/ and $' and $", so all these sequences now pass through the template without escape.
  • Line numbers are aligned exactly so that both syntax errors and runtime errors in exception traces are reported on the correct line of the template file in which the code appears.

Together these small changes - particularly accurate error line numbers - make @stringfunction much more usable for composing large templates for website development.

Usage

Usage is unchanged: just annotate a python function with @stringfunction or @unicodefunction, and then put the template text where the docstring would normally be. Leave the function body empty, and efficient code to concatenate the contents will be created.

  from templet import stringfunction
  
  @stringfunction
  def myTemplate(animal, body):
    "the $animal jumped over the $body."
  
  print myTemplate('cow', 'moon')

This is turned into something like this:

  def myTemplate(animal, body):
    out = []
    out.append("the ")
    out.append(str(animal))
    out.append(" jumped over the ")
    out.append(str(body))
    out.append(".")
    return ''.join(out)

There are just six constructs that are supported, all starting with $:

  • $myvar inserts the value of the variable 'myvar'
  • ${...} evaluates the expression and inserts the result
  • ${[...]} runs a list comprehension and concatenates the results
  • ${{...}} executes enclosed code; use 'out.append(text)' to insert text
  • $$ an escape for a single $
  • $ (at the end of the line) is a line continuation

All ordinary uses of $ in the template need to be escaped by doubling the $$ - with the exception of (as mentioned above) $., $(, $/, $', and $".

Philosophy

The philosophy behind templet is to introduce only the concepts necessary to simplify the construction of long strings in python; and then to encourage all other logic to be expressed using ordinary python.

A @stringfunction function can do everything that you can do with any function that returns a string: it can be called recursively; it can have variable or keyword arguments; it can be a member of a package or a method of a class; and it can access global imports or invoke other packages. As a result, although the construct is extremely simple, it brings all the power of python to templates, and the @stringfunction idea scales very well.

Beyond simple interpolation, templet does not invent any new syntax for data formatting. If you want to format a floating-point number, you can write ${"%2.3f" % num}; if you want to escape HTML sequences, just write ${cgi.escape(message)}. Not as brief as a specialized syntax, but easy to remember, brief enough, and readable to any python programmer.

Similarly, templet does not invent any new control flow or looping structures. To loop a template, you need to use a python loop or list comprension and call the subtemplate as a function:

 @stringfunction
 def doc_template(table):
   """
   <body>
   <h1>${ table.name }</h1>
   <table>
   ${{
     for item in table:
       out.append(self.row_template(item))
   }}
   </table>
   </body>
   """

If you prefer list comprehensions, it is slightly more brief:

 @stringfunction
 def doc_template(table):
   """
   <body>
   <h1>${ table.name }</h1>
   <table>
   ${[self.row_template(item) for item in table]}
   </table>
   </body>
   """

The design encourages simple templates that read in straight-line fashion, an excellent practice in the long run. Although when invoking subtemplates you need to pass state, of course you can use @stringfunction to make methods and pass state on "self", or use object parameters.

Details and Style

Some tips/guidelines for using these annotations.

Whitespace can be important inside HTML, but for python readability you often want to indent things, so @unicodefunction / @stringfunction gives you a few tools:

  1. It identifies the number of leading spaces that are uniformly used to the left of the template and strips them.
  2. It strips the first line of the template, if empty.
  3. It allows you to use a $ at the end of a line for a line continuation.

So my recommended style for multiline templates is:

  • indent template text in the function as if it were python code.
  • use a python triple-quote and put the opening quote on its own line.
  • never indent HTML tags - they just get too deep, so put them all at column 0.
  • when nesting gets confusing, for readability, just put one tag on each line.
  • liberally use $ continuations if layout demands no-whitespace.
  • indent code inside ${{ and then put }} on its own line (a newline right after a closing }} is eaten).

Relative indenting for python code inside ${{...}} is preserved using the same leading-space-stripping trick as is used for the templates themselves, so you can indent embedded python as normal, and you can start the indenting at whichever column feels natural. I usually indent embedded python by one more level.

In the unusual case where it is necessary to emit text that has leading spaces on every line, you can begin the template with a continuation line with the $ in the column that you want to treat as column zero.

One question is whether the opening """ should be on the same line as the def or its own line. Either style is supported - for line number purposes, the program source is just scanned to discover the position of the opening quote - but for clarity I usually put the opening quote on its own line.

For example, if you want to achieve all on one line the following:


<tr><td><a class="/foo/bar/...">....</a></td><td>...</td></tr>

Then you could use:

@unicodefunction
def table_row(row_data):
  """
  <tr>$
  <td>$
  <a$
   class="/foo/bar/${cgi.escape(filename, True)}">$
  ${cgi.escape(link_text})}$
  </a>$
  </td>$
  <td>$
  ${{
    if (enrolled): out.append('enrolled')
  }}
  ${cgi.escape(label_text)}$
  </td>$
  </tr>
  """

Posted by David at 01:08 PM | Comments (2)

March 02, 2011

PUT and DELETE in call.jsonlib.com

My son was trying to use call.jsonlib.com to access the Google Docs API and told me he needed the ability to execute HTTP PUT and DELETE, not just GET and POST.

Done. Now call.jsonlib.com/fetch supports a "&method=" CGI argument that you can set to PUT or DELETE, and Anthony reports that it's working fine for accessing the API. (Does anybody know of any simple public HTTP servers out there that will echo your request so that it can be easily tested? At most URLs, using these unusual methods will just give you a 405 error.)

Access-Control-Allow-Origin

Another change is that call.jsonlib.com now supports cross-domain access via XHR (not just jsonp) on browsers that support the Access-Control-Allow-Origin header.

The problem with JSONP is that you can only use the GET method, not POST, so all of your submitted data must fit in a URL string. Some browsers, servers, and proxies may have trouble handling URL strings longer than about 2K, so this means you cannot use JSONP to reliably submit long chunks of data.

The solution is to go back to using traditional XHR techniques, but to tell your browser that cross-origin access is OK using the Access-Control-Allow-Origin header. This header is supported on modern browsers (FF, Chrome, Safari, Opera, and IE8+).

You can write code like the following (jQuery) to use the POST method to interact with jsonlib:

$.post('call.jsonlib.com/fetch',
  { url: 'davidbau.com/data/animals' },
  function(m) { output(m); },
  'json');

The above just uses POST to talk to jsonlib, and asks jsonlib to do a GET, but obviously you can use a POST to ask jsonlib to do a POST or a PUT as well.

Sniffing Character Encodings

The other update is logic for sniffing encodings. Since call.jsonlib.com always returns its contents as JSON unicode strings, it needs to understand how to convert responses from arbitrary other encodings. For text documents, it now uses BeautifulSoup and the chardet library to automatically detect encodings and convert text and html documents to unicode. This behavior can be overridden using the &encoding= CGI argument.

Examples: fetching utf-8 smart quotes and fetching cp-1252 smart quotes.

Non-text documents are treated as iso-8859-1 which is the simplest way use unicode 0-255 codepoints as a way of storing a bytestream (example: fetching a jpeg.)


Posted by David at 06:52 AM | Comments (2)

February 24, 2011

Party like it's 1789

What is going on in the world? It is like the 18th century out there.

The information-dominated 21st century is different from the nuclear-tipped 20th. In today's world, the power of the message is what matters.

Will historians see the 21st century as a second Enlightenment?


Posted by David at 06:43 PM | Comments (0)

January 30, 2011

Using goo.gl with jsonlib

An example using jsonlib to invoke an inconvenient HTTP API from Javascript.

When Google released the goo.gl URL shortener API recently, they did not provide a JSONP API, so it is not convenient to make goo.gl URLs directly from Javascript. But then I wanted to implement a goo.gl-based "Save As Short Url" feature in the save box of Heidi's Sudoku Chrome App. The app is 100% Javascript and isn't even hosted on its own web server. What to do?

It is easy to do cross-domain HTTP by bouncing JSONP requests off call.jsonlib.com. With jsonlib, invoking the goo.gl API is just a few lines of code.

How To Do It

Here is how the goo.gl API works:

  1. You assemble a JSON string containing your long URL in the 'longUrl' field.
  2. This string must be POSTed to the googleapis server, with Content-Type "application/json".
  3. The response is JSON that will have your short URL in the 'id' field.

In Javascript, it is inconvenient to POST data that is not formatted as "application/x-www-form-urlencoded", and it is hard to use cross-domain HTTP requests that do not return script-formatted JSONP services. However, call.jsonlib.com solves both of these problems easily.

Here is the code for a goo.gl shortener wrapper that shows how it can be done with a single call to jsonlib.fetch:

<script src="/img/spacer.gif"> </script>
<script>
function googlurl(url, cb) {
  jsonlib.fetch({
    url: 'https://www.googleapis.com/urlshortener/v1/url',
    header: 'Content-Type: application/json',
    data: JSON.stringify({longUrl: url})
  }, function (m) {
    var result = null;
    try {
      result = JSON.parse(m.content).id;
      if (typeof result != 'string') result = null;
    } catch (e) {
      result = null;
    }
    cb(result);
  });
}
// Make a short URL for a nicely written book.
googlurl('www.amazon.com/Numerical-Linear-Algebra-Lloyd-'
       + 'Trefethen/dp/0898713617', function(s) { alert(s); });
</script>

Voila! Short URLs in a single function call.

Play with the code here if you like.


Posted by David at 11:53 AM | Comments (1)

January 29, 2011

Simple Cross-Domain Javascript HTTP with call.jsonlib.com

In Python, fetching any webpage in the world is a one-liner:

print urllib2.urlopen('davidbau.com/data/animals').read()

But Javascript's same origin policy prevents you from doing the same thing in Javascript unless your script happens to be running on a page from the same domain. The SOP is intended to protect the security of the user by limiting access to private resources such as:

  1. Private browser state like cookies from other domains.
  2. Private web pages that may be only accessible behind firewalls.

While this is a nice thing for protecting logged-in banking sessions and secrets on corporate LANs, it also means that plenty of perfectly safe and useful network code cannot be written with Javascript. You need to do your cross-domain networking server-side, and then bounce your requests off a server in your own domain.

JSONP Lets Servers Expose Services Cross-Domain

Interestingly, the "src" attribute of the <script> tag is not subject to the same origin restriction, which means that servers can intentionally expose services cross-domain by encoding their data as a javascript function call.

In 2005 Bob Ippolito proposed standardizing this convention as a format called JSONP. Today, JSONP can be used to make cross-domain calls to various useful APIs including Flickr, GData, Twitter, and YQL. JSONP is incredibly useful and is supported in all the major AJAX libraries such as JQuery.

YQL in particular is very versatile, and it actually allows you to make your own further HTTP requests to other domains. However, it does a lot more and is a bit complicated and slow as a result. So for the specific purpose of making secure cross-domain HTTP calls on the public internet, I have been using a service I've posted on Google App Engine, called call.jsonlib.com.

Cross-Domain Javascript HTTP Requests on the Public Web

The library at call.jsonlib.com lets you get very close to the python one-liner:

jsonlib.fetch('davidbau.com/data/animals', function(m) { alert(m.content); });

It is just a simple packaging of the python urllib2 library as a JSONP library. But it is tremendously useful. It allows you to:

  • Make GET or POST requests to arbitrary public servers
  • Set arbitrary HTTP headers
  • Read all the HTTP headers of the response

Because all the requests are made from the context of a server on the public internet, it does not expose your private LAN or private cookies to security holes.

A few corners are smoothed out to make use from javascript easier. For example:

  • jsonlib.fetch is intelligent about encodings and converts the content to unicode based on HTTP headers, content sniffing, and <meta> tags in the HTML content, if any.
  • jsonlib.fetch can also do basic scraping tasks server-side such as extracting elements that match a particular css selector, or stripping HTML markup and returning just text.
  • jsonlib.fetch is careful to access the proxy using https if the underlying URL being scraped is https, so that the whole path is encrypted.

More documentation here.

Some examples here.


Posted by David at 06:47 AM | Comments (4)

January 22, 2011

Dabbler Under Version Control

I notice that my son's friends have been very active on dabbler.org recently, so I've placed it under version control so that they don't lose more than a day of work if there is an accident.

The dabbler svn repository is here, with automatic check-ins every night.


Posted by David at 07:52 AM | Comments (0)

January 12, 2011

Snowpocalypse Hits Boston

spacer

We'll need to dig out in Lincoln MA. All this snow arrived last night. It's still coming down!


Posted by David at 10:14 AM | Comments (0)

December 30, 2010

Heidi's Sudoku Hintpad

spacer I have just posted Heidi's Sudoku Hintpad, a Chrome Sudoku web app for playing the popular pencil-and-paper puzzle.

This version of Sudoku is inspired by Heidi's favorite way to solve Sudoku: instead of starting by eliminating candidates one square at a time, she loves to speed along looking for digits that can only be placed in one location because they are blocked by the same digit in various rows, columns, or blocks.

Hints, not Answers

The idea of the Sudoku Hintpad is to give you several different types of hints that don't give away the puzzle.

For example, after a mistake it is easy to get lost. You can click "Check" to quickly look for obvious mistakes, and ctrl-Check checks squares against the final answer without revealing the answer. The brower's "back" button can be used to undo as many steps as needed.

If are stuck you can click "Hint" to highlight a few specific squares to mull over. The hintpad will show squares that constrain the puzzle in some way that should allow you to make a deduction. Ctrl-Hint makes the hint more explicit by pointing to squares that should be solvable after you're done thinking.

No Boring Parts

If you think the task of crossing out candidates is boring, there is a "Pencilmarks" button that does it for you automatically. Or do it for one specific square by clicking the "?" on the entry box. Once you have pencilmarks, the "Hint" button can help you find naked sets, hidden sets, x-wing formations, and so on.

The hintpad will supply random Sudoku or load them from Gordon Royale's minimum sudoku collection; or any Sudoku puzzle can be entered directly.

Credits

The hintpad is assembled using John Resig's jQuery with Ben Alman's handy BBQ plugin to manage "back" button state. Kimberly Geswein's terrific Covered-By-Your-Grace font provides digits that look just like Heidi's handwriting, and Gordon Royale's Minimum Sudoku Collection provides minimal 17-hint puzzles when you hold control and click "New Puzzle." The app's sources are here, here, and here. From beginning to end (not including jquery and bbq), it is a complete app in about 1900 lines of code.


Posted by David at 07:29 PM | Comments (8)

December 11, 2010

Social Responsibility in Tech

Rebecca MacKinnon's recent column is worth reading.

In a day where the New York Times is displaced by Netflix on the S&P 500 and where Chrome OS portends the end of the PC and the rise of the cloud, we need to realize that it is time for the tech business to grow up.

The electronic spaces in which 21st century citizens exercise their right to Free Speech and Peaceable Assembly are internet communities that are run by for-profit private corporations.

As the Times and the Post and the Journal downsize and struggle, the pressing question of the day is not whether Google or Facebook or Twitter or Youtube or Amazon can make the next billion dollars; but whether we in the tech world have the fortitude to take our newfound social responsibility seriously.


Posted by David at 10:00 AM | Comments (2)
gipoco.com is neither affiliated with the authors of this page nor responsible for its contents. This is a safe-cache copy of the original web site.