So, this is my first post in some time. Today I'd like to involve you in another of my interests - contemporary classical music. I have provided a detailed commentary of the Opera below.
My favorite composer of this genre is the esteemed American composer John Adams, who is perhaps best known for his modern operas, particularly Nixon in China and The Death of Klinghoffer.
Nixon in China has long been one of my favorite operas. It retells the story of President Nixon visiting China and Chairman Mao in 1972. It begins with a chorus of revolutionaries recounting the duties of Chinese communist soldiers from the civil war, and then moves into a lyrical and revolutionary anthem (Watch here):
The people are the heroes now,
Behemoth pulls the peasants plow,
When we look up, the fields are white,
The fields are white with harvest in the morning light
And mountain ranges one by one, rise red beneath the harvest moon
The images of mighty landscapes of China are ones that recur frequently throughout Alice Goodman's libretto. Similar language was used by the Communists of the era, to draw attention to, and build support for, Chinese nationalism.
After the revolutionaries' chorus ends, we see the arrival of Nixon in Air Force One (Watch here). The music at this section is perhaps some of the most fitting in the entire opera, and draws attention to the theatre that is in politics. In this and other performances, the audience feels compelled to applaud as Nixon steps out of the plane. Adams makes frequent use of this almost-cabaret brass as a satirical tool throughout the opera.
Far from the serious, ominous tone of the revolutionaries, we instead see a Nixon obsessed with his own media appearance. Instead of being concerned with the substance of his actions, he is far more concerned with their apparent magnitude:
News, News, News, News,
It's prime time in the USA! USA! USA!
This sets up Nixon as a pitiful character in the eyes of the audience, the presentation of his frivoulousness as grand opera with a busy accompaniment only amplifies this.
When the character of Mao is introduced, once again, we see a focus on political philosophy:
Nixon: Let us turn our talk towards Taiwan, Vietnam, and the problems there.. Japan
Mao: Save that for the premier! My business is philosophy.
Mao's brisk and imperious nature is amplified by the three women that follow him around, echoing his every word.
And, of course, Goodman is sure to include real historical references, that help to place the Opera into context:
Mao: Six Crises isn't a bad book.
Nixon: He reads too much.
Zhou: Ah, who can say.
Mao's remark, "Six Crises isn't a bad book" is subject to much debate. In Hunan Chinese (which Mao exclusively spoke), "not bad" doesn't mean "good", and lacks the positive connotation that exists in English. The implication of the words "Not bad" is that of mediocrity, not substance. Of course, Nixon took it as a compliment, but most are unsure what Mao's original meaning was. Later on, Mao follows up with another remark true to history, a piece of Cultural Revolution rhetoric that was responsible for the defacement of Confucius' grave, among other things -- "We no longer need Confucius."
The musical accompaniment behind Mao is very busy, and at first reminded me of the mechanical, almost steam-engine sound of early industry. Mao was a well-known industrialist, and so the music interplays very well with the libretto:
Platonic men came from the caves of Pao An,
Want to spend their lives in the daylight,
Want to hear the sound of industry borne on the wind,
Want to hear the sound, want to hear the sound
of industry borne on the wind.
The plow breaking the furrow,
Cloth pierced by the needle,
Giant earth-movers, and these men want to work,
Not turn back, dazzled, to the dark,
Echoes, shadows, and chains
The first act concludes with various addresses of the various dignitaries at the signing of the Shanghai Communique. Premier Zhou Enlai kicks them off with a sublime aria, that establishes Zhou's character as a sly, but conscientious diplomat, finishing with a round of triumphant toasts.
All patriots were brothers once,
Let us drink to the time when they shall be brothers again!
Gan bei! Gan bei! Gan bei!
Finally, Nixon begins the "Cheers" scene (Watch Here), a highly frivolous scene, both in music, and in Libretto:
Zhou: To The USA!
Nixon: To Chairman Mao!
Zhou: To The USA!
Nixon: To Chairman Mao!
(Cheers, Cheers, Cheers)
The ending of this act highlights the ultimate pointlessness of the Shanghai Communique, and the lack of diplomatic progress made by Nixon.
The Second act begins with Pat Nixon and a chorus of communist women. Pat Nixon's text reflects on suburban life in America, while the communists evoke images (once again) of the Chinese landscape:
Look down at the earth.
A living current moves beneath rivers,
Caught in the hand of death.
Serpentine mountains cross the plain,
To bask in an uncertain sun.
And elephantine hills rejoice
Advancing towards a sky of ice
Here Goodman uses similar imagery to Chairman Mao's own poetry, from Changsha:
Alone I stand in the autumn cold
On the tip of Orange Island,
The Xiang flowing northward;
I see a thousand hills crimsoned through
By their serried woods deep-dyed,
And a hundred barges vying
Over crystal blue waters.
Eagles cleave the air,
Fish glide under the shallow water;
Under freezing skies a million creatures contend in freedom
Similar imagery is used throughout the entire opera.
After Pat Nixon has finished her scene, various characters from the Opera take on roles from the play of Mme. Mao, The Red Detatchment of Women. As the opera adaptation of this play is performed, the music becomes almost a caricature. The dramatic sounds of the Tyrant (played by Henry Kissinger) sound like a villain from an old television show. Just as the play was concluding, Jiang Qing herself barges onto the scene, shouting, "That is your cue!".
After this, the tone of the Cultural Revolution (largely absent from the first act, as the Chinese hid it from Nixon), begins to show through. Qing sings (Watch Here):
I am the wife of Mao Tse-tung
I am the wife of Mao Tse-tung who raised the weak above the strong,
When I appear the people hang,
When I appear the people hang upon my words!
Subtle play of words aside, her ferocity and revolutionary anger increases as she continues, brandishing her little red book:
I speak according to the book! The book! The Book!
(Chorus: The Book The Book The Book!)
The Book!
The Book!
As she stands triumphant, the people begin torturing and expressing their anger against, quote, "counter-revolutionary elements".
The final act (or really, the final part of the second act), is of a more reflective tone (Watch Here). As Nixon reflects on his time in World War Two, Mao and his wife begin to reflect on their romance, and Zhou begins to feel nihilistic:
We fight, we die
And if we do not fight, we die
The contrast of these reflections is drawn heavily by the accompaniment, which in particular, draws in the Cabaret style and dancers, perhaps calling attention to Jiang Qing's prior career as an Actor.
Anyway, I don't really know how to wrap this commentary up, so I'll leave it there. Hopefully someone found it interesting.
Over the past few days I've been playing a fairly popular game for Google's Android platform on my phone. The game is called Shot. The rules of the game are fairly simple.
In a 7x9 grid, we have a variety of balls at certain positions.
Here's a simple example:
o
o o o
o
o o o o
o o
o
The object of the game is to knock all the balls (except one) out of the grid. We can knock a ball in any of the four directions (up, down, left, right), subject to the following restrictions:
1. You can't move a ball off the edge of the map directly, you must instead send a ball on a collision course with another ball:
Illegal
↑
o
o
Legal
o
↓
o
2. You can't move a ball into a ball that is adjacent to it. So moving A down to strike B is illegal here, but moving A to C is legal.
A→ C
B
3. When a ball strikes another ball, the moving ball stops, and the stationary ball begins moving in the same direction. Once this happens, Rules 1 and 2 no longer apply for the moving ball.
Example: This move:
o→o oo
Results in this playing out:
oo→oo
o ooo→
o oo
And hey! We've lost a ball. Rince and repeat to solve the puzzle:
Move 2:
o ←oo
←oo o
o o
Move 3:
o→o
o
And we solved the puzzle! Admittedly, that example wasn't hard, but I'm sure you can imagine difficult examples (If you can't, play the game!).
I have an annoying tendency with puzzle games to become frustrated with them, to the point of writing solvers for them to save myself the trouble. Of course, once the solver is written, the game is ruined, but at least the solver was fun to write (don't ever ask me to solve a sudoku puzzle =P).
As with all Literate Haskell posts, let's begin the post with our imports, which you can ignore, but are there for the compiler and posterity.
1
2
3
4
5
6
7
| module Main where
import Data.Maybe import Data.List import Prelude hiding (Left, Right) import Control.Arrow import Control.Monad
|
Okay, so, we're going to need some form of data type to represent the board. I chose a list of ball coordinates over a grid structure, sacrificing a bit of time efficiency in exchange for being able to use the lovely Data.List functions.
1
2
3
4
5
6
7
| data Ball = Ball { ballX :: Int , ballY :: Int } deriving (Show,Eq)
type Board = [Ball]
|
We'll also need a data type to represent a single step or move in the game. I represent it as:
- the ball that was moved
- the direction is was moved in
1
2
3
4
5
6
7
8
9
| data Direction = Up | Down | Left | Right deriving (Show,Eq)
data Step = Step Ball Direction deriving (Show,Eq)
|
Finally, we will use the Maybe type to encapsulate a series of Steps to represent a solution. Nothing means that there is no solution. In any other case, the series of steps provided will solve the puzzle.
1
2
| type Solution = Maybe [Step]
|
Okay, enough of data types. Let's get on to solving algorithms. Seeing as each move in the game removes exactly one ball from the grid, and the object is to remove n - 1 balls from the grid (where n is the number of balls in the puzzle), I deduced that all solutions have the same length (n - 1), and hence a simple recursive backtracking algorithm will suffice.
That is, given a game state, deduce all the legal moves that can be made, then recursively call on each one with an updated gamestate reflecting the move, stopping after a valid solution is found.
Seeing as Solution is a Maybe type, I use Control.Monad's mplus operator, which, specifically for Maybe values, could be implemented like this:
1
2
3
4
5
6
| mplus :: Maybe a -> Maybe a -> Maybe a mplus (Just a) _ = a mplus (Nothing) (Just a) = a mplus (Nothing) (Nothing) = Nothing
|
That is, it returns the first Just value it sees, and ignores all Nothing values. Applied to the solver problem, this means I can use it with foldr to select a solution.
1
2
3
4
5
6
7
8
9
10
| solve :: Board -> Solution solve board | isSolved board = Just [] solve board = foldr mplus Nothing $ map solver legalMoves where solver m = case solve (applyMove m board) of Just r -> Just (m:r) Nothing -> Nothing legalMoves = filter (isLegal board) possibleMoves possibleMoves = concatMap (flip map [Up, Down,Left,Right]) (map Step board)
|
This function used alot of yet-undefined functions, the first being isLegal, that determines if a given move, given a certain state, is legal. In this function, I use Control.Arrow's >>> operator, which, for functions, is the same as flip (.) . As discussed in the preamble, the two conditions for a move to be legal are:
- The move will be stopped by another ball before reaching the edge of the screen.
- The move is not trying to move into an adjacent ball.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| isLegal :: Board -> Step -> Bool isLegal board (Step ball@(Ball x y) dir) = notAdjacent && willBeStopped where willBeStopped = any check board check = and . \v -> map ($ v) $ checkFor dir checkFor Up = [ballX >>> (==x), ballY >>> (< y)] checkFor Down = [ballX >>> (==x), ballY >>> (> y)] checkFor Left = [ballX >>> (< x), ballY >>> (==y)] checkFor Right = [ballX >>> (> x), ballY >>> (==y)]
notAdjacent = nextBall `notElem` board nextBall = newCoords dir ball
newCoords :: Direction -> Ball -> Ball newCoords Up (Ball x y) = Ball x (y-1) newCoords Down (Ball x y) = Ball x (y+1) newCoords Left (Ball x y) = Ball (x-1) y newCoords Right (Ball x y) = Ball (x+1) y
|
(the newCoords function is top-level as it is used elsewhere later on)
Naturally, the condition for a solved board is that there is only one ball left:
1
2
3
| isSolved :: Board -> Bool isSolved = length >>> (== 1)
|
(Once again I have used the >>> operator. I think it looks clearer than (\x -> length x == 1) or (==1) . length .
Finally there is the applyMove function, which, given a move and a board, updates the board to reflect the move. This function works as follows:
- Calculate the next position of the ball (the
moveBall helper). - Update the ball's position in a new board.
- If the ball just struck another ball, recursively call oneself, this time moving the struck ball in the same direction.
- If the ball has gone outside the bounds of the grid, remove it.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| applyMove :: Step -> Board -> Board applyMove step board = filter (not . outsideBounds) $ applyMoveHelper step board where applyMoveHelper (Step ball dir) board = let movedBall = moveBall dir ball nextBall = newCoords dir movedBall nextBoard = movedBall:delete ball board in if nextBall `elem` board then applyMoveHelper (Step nextBall dir) nextBoard else nextBoard
moveBall dir ball | outsideBounds ball = ball moveBall dir ball = let nextBall = newCoords dir ball in if nextBall `elem` board then ball else moveBall dir nextBall
outsideBounds (Ball x y) = x > 6 || x < 0 || y > 7 || y < 0
|
Finally, we have two functions used in the IO processing. One to convert strings to a Board representation, and one to do the reverse (with a border). The format looks something like this:
# #
# #
#
Producing the data structure: [Ball 0 0, Ball 2 0, Ball 1 1, Ball 3 1, Ball 1 3]
The strToBoard function uses a clever algorithm that assigns numbers (with zip) to each line and each character, then filters out all the non-hash characters, resulting in a simple set of coordinates that can be converted to a Board structure easily.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| strToBoard :: String -> Board strToBoard str = rows (lines str) where rows lines = concatMap cols $ zip lines [0..] cols (l,y) = map (flip Ball y .snd) $ filter (fst >>> (=='#')) $ zip l [0..]
showBoard :: Board -> String showBoard board = "+++++++++\n" ++ unlines (map (rowToString) groupedCoords) ++ "+++++++++\n" where rowToString = (++"+") . ('+':) . map charFor groupedCoords = groupBy (\a b -> snd a == snd b) allCoords allCoords = [(x,y) | y <- [0..8], x <- [0..6]] charFor (x,y) = if Ball x y `elem` board then '#' else ' '
|
Finally, there remains anther output function to display a Solution in a way that can be digested by humans and copied into the game to solve a puzzle.
1
2
3
4
5
6
| prettyPrint :: Board -> [Step] -> String prettyPrint board moves = concatMap pretty $ zip moves $ boards where boards = zipWith applyMove moves (scanl (flip applyMove) board moves) pretty ((Step (Ball x y) dir),board) = show (x,y) ++ "=> " ++ show dir ++ "\n" ++ showBoard board
|
The main function is then very straightforward, using the interact function that runs a String->String function through IO.
1
2
3
4
5
| main :: IO () main = interact solver where solver str = let board = strToBoard str in prettyPrint board $ fromJust $ solve board
|
You can download the unliterate Haskell version of this here!