I wanted to provide the simplest introduction that would motivate someone to learn more while not seeming too difficult. I decided that, clearly, Tic-Tac-Toe would be the best demo. The idea is the person would sit at my side while I wrote the program. I would explain each step, with my pupil eventually telling me what the final bits should be.
The challenge
Initially I assumed Python would be the best choice for this, since it reads like pseudo-code and is my go-to language. But after a couple of days I realized that text printing just doesn't excite people like interactive graphics, no matter how simple. I could do this in Python using something like pygame, but I don't know that API and it's hardly the lean introduction I'm looking for. It seemed that the best way to demo Tic-Tac-Toe would be using JavaScript, HTML, and CSS.
As a fun challenge, I wrote the same introductory Tic-Tac-Toe program in JavaScript and Python. I went as fast as I could, and made no more changes as soon as the program worked correctly. I used jQuery for JS and nothing special for Python. In both cases it took me about 20 minutes from nothing to done. The JavaScript/HTML/CSS page is 140 lines. The Python program is 80 lines. The code for both is pretty ugly.
Here's some sample output from the Python program:
X's turn (type xy): 21 1 2 3 +---+---+---+ 1 | | X | | +---+---+---+ 2 | O | X | | +---+---+---+ 3 | | | | +---+---+---+Here's the entire JavaScript program in an iframe:
The Outcome: Reevaluating
Though I enjoy writing JavaScript because of what it enables, I hate its gotchas and limitations. In contrast, I enjoy Python thoroughly as a language, and only get frustrated by its occasional bottlenecks. Before this project I've assumed that JS would be awful for beginners. When I read that Khan Academy would be teaching CS with JS I thought that was a bad plan.
But now I think I was wrong. Getting beginners interested in learning programming is the hard part. Once they want to learn, programming itself is rewarding enough that it becomes addictive. So all you need is the right hook. And I find the graphical Tic-Tac-Toe demo to be that so much more than the command-line one.
Postscript
For reference, here's the Python code:
def run(): board = [ [' ', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' '], ] color = 'X' print 'To play, type the coordinates of where you want to go.' print 'For example, the middle square is 22, the top right is' print '31, and the bottom left is 13.' while True: print print ' 1 2 3' print ' +---+---+---+' for i, row in enumerate(board): row = tuple([i+1] + row) print '%d | %s | %s | %s |' % row print ' +---+---+---+' # Figure out where the move is command = raw_input("%s's turn (type xy): " % color) if not len(command) == 2: print 'Bad command' continue try: column = int(command[0]) row = int(command[1]) except ValueError: print 'Command not numbers' continue if not (3 >= column >= 1): print 'Bad column' continue if not (3 >= row >= 1): print 'Bad row' continue column -= 1 row -= 1 if board[row][column] != ' ': print 'Illegal move' continue # Apply the move and swap the player board[row][column] = color if color == 'X': color = 'O' else: color = 'X' # Check for a winner # Horziontal for row in board: if row[0] != ' ' and (row[0] == row[1] == row[2]): print '%s wins!' % row[0] return # Vertical for i in xrange(3): if board[0][i] != ' ' and (board[0][i] == board[1][i] == board[2][i]): print '%s wins!' % board[0][i] return # Diagnol if board[0][0] != ' ' and (board[0][0] == board[1][1] == board[2][2]): print '%s wins!' % board[0][0] return if board[0][2] != ' ' and (board[0][2] == board[1][1] == board[2][0]): print '%s wins!' % board[0][2] return run()And here's the JavaScript code:
<!doctype html> <html> <head> <meta charset="utf-8"> <title>Existential Crisis: Tic-Tac-Toe</title> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script> <style type="text/css"> table td { width: 200px; height: 200px; border: 1px solid #000; text-align: center; font-size: 108px; vertical-align: middle; } table { border-collapse: collapse; } .play-X { color: #000; } .play-O { color: #f00; } </style> </head> <body> <h1>Tic-Tac-Toe</h1> <table> <tr> <td data-row="0" data-column="0"> </td> <td data-row="0" data-column="1"> </td> <td data-row="0" data-column="2"> </td> </tr> <tr> <td data-row="1" data-column="0"> </td> <td data-row="1" data-column="1"> </td> <td data-row="1" data-column="2"> </td> </tr> <tr> <td data-row="2" data-column="0"> </td> <td data-row="2" data-column="1"> </td> <td data-row="2" data-column="2"> </td> </tr> </table> <script type="text/javascript"> var WINNER = false; var BOARD = [ ['', '', ''], // [column 0, column 1, column 2] ['', '', ''], ['', '', ''] ]; var COLOR = 'X'; $('td').click(function(e) { if (WINNER) { return; } // Mark the move, if legal. var target = $(e.target); var column = parseInt(target.data('column')); var row = parseInt(target.data('row')); if (!BOARD[row][column]) { BOARD[row][column] = COLOR; } else { alert('Illegal move!'); return; } // Toggle who's playing. if (COLOR == 'X') { COLOR = 'O'; } else { COLOR = 'X'; } // Update board state. $('td').each(function() { var cell = $(e.target); var column = parseInt(cell.data('column')); var row = parseInt(cell.data('row')); if (BOARD[row][column]) { cell.text(BOARD[row][column]); cell.addClass('play-' + BOARD[row][column]); } else { cell.html(' '); cell.attr('class', ''); } }); // Check for a winner. // Horizontal for (var i = 0; i < 3; i++) { if (BOARD[i][0] && BOARD[i][0] == BOARD[i][1] && BOARD[i][1] == BOARD[i][2]) { alert(BOARD[i][0] + ' has won!'); WINNER = true; return; } } // Vertical for (var i = 0; i < 3; i++) { if (BOARD[0][i] && BOARD[0][i] == BOARD[1][i] && BOARD[1][i] == BOARD[2][i]) { alert(BOARD[0][i] + ' has won!'); WINNER = true; return; } } // Diagnol if (BOARD[0][0] && BOARD[0][0] == BOARD[1][1] && BOARD[1][1] == BOARD[2][2]) { alert(BOARD[0][0] + ' has won!'); WINNER = true; return; } if (BOARD[0][2] && BOARD[0][2] == BOARD[1][1] && BOARD[1][1] == BOARD[2][0]) { alert(BOARD[0][2] + ' has won!'); WINNER = true; return; } }); </script> </body> </html>You can find code for both on github.