Category: Programming

Multi-Cursor Editing in Xcode 10

Multiple selection support in Xcode 10 is amazing. In previous versions, you could hold down the Option key to select a column of text, but you couldn’t do much with it. In Xcode 10, typing in such a multi-selection finally does the right thing: your text goes into each selection, so you can update multiple places at once. This alone really speeds up editing. But until recently, I had missed that multi-selection support goes far deeper:

  • Shift-Control to add selection: Hold down Shift and Control while clicking or dragging to add a new selection. So if you need to make the same edit in multiple places, just select each one, then start typing and make all the edits at the same time.
  • Option-Command-E to add the next occurrence of the current selection: If you already have something selected that you were going to type over, use Option-Command-E as needed to add selections at other places where this occurs, and edit just once.
  • Move multiple selections: As you’re editing, use the arrow keys to move multiple selections in unison; use Option to move by words, use Control to move by subWordsLikeThis (you may need to turn off System Preferences > Keyboard > Shortcuts > Mission Control > Move left/right a space).
  • Copy/paste: Create multiple selections, Copy, set multiple insertion points, Paste. Works like magic.
  • Use Find to create selections: Look in the Find menu for more tools. For example, Select All Find Matches and Select Find Matches in Selection let you set up multiple selections based on your search text.

This can all be accomplished by other means, but multiple selections fit naturally with the way I work, and don’t require extra mental effort or a context shift. This feature has been invaluable in moving some code from C++ to Swift. Thanks to the Xcode team for getting this right.

Happy editing!

Double-Tap to Zoom

Such a simple feature: double-tap to zoom in on the Go board. On the iPhone, it conveniently zooms to use the full height available. So why does SmartGo Kifu have a setting to turn it off?

Double tap to zoom

When SmartGo detects a tap on the board, your intent is not clear: do you plan to play a move, or is there a second tap coming? On iOS you can tell the system to disambiguate between the two gestures – for the single-tap to succeed, the double-tap has to fail:

singleTapGesture.require(toFail: doubleTapGesture)

The price you pay is that move entry is delayed by a quarter of a second. Thus that setting gives you a choice: convenient zooming or faster move entry. Not a good choice. (But at least you now know how to speed up move entry.)

There’s a better way to solve this: always play the move right away. Then if it turns out to be the first tap in a double-tap, undo the move and zoom the board instead. Fast move entry and zoom, no setting needed. However, that means being able to cleanly undo the move (and anything else that a tap on the board might do), and in SmartGo Kifu, that would be harder to implement than what the feature is worth.

Rewriting SmartGo in Swift is still work in progress; a long journey. But thanks to less technical debt, I’m able to add features like undo, and thus make move input fast while still allowing you to double-tap. Getting rid of a confusing setting always makes me happy.

SmartOthello Postmortem

SmartOthello is not dead yet, but the app has not lived up to expectations. It’s time for an assessment of this project: why I started it, obstacles along the way, and where to go from here.

Motivation

I needed a project to learn Swift and to experiment with different business models. I settled on Othello for several reasons:

  • Expertise: Expert knowledge of the game, and contacts in the Othello community. (I was the 1992 US Othello champion and have played at six Othello world championships.)
  • Similar domain: A two-player board game close enough to Go that much of the foundation code could carry over.
  • Potential customers: A more popular game than Go (think of all the people playing it as Reversi on Windows).

In addition, a trademark dispute had recently been resolved, with Megahouse of Japan making more efforts to license use of the name “Othello” and the trademarked board design.

Expectations

My three Go apps (SmartGo Kifu – $20, SmartGo Player – $3, and Go Books – free with IAP) were all doing reasonably well, with roughly similar profits. I had hoped SmartOthello might add a fourth leg to that stool, and thought I had several things going for me:

  • Experience: I had years of experience with Go apps in the App Store.
  • Marketing: I had a good marketing angle (former US Othello champion).
  • Design: All the existing Othello/Reversi apps in the App Store sucked. I knew I could create an app that gave players a better experience. (And some of those apps were ranked similar to my Go apps on the top grossing charts.)
  • Social: Game Center online play and leaderboards should help spread the app socially, something I didn’t have in my Go apps.

Obstacles

I made good progress on the app in the fall of 2015, learning Swift along the way. Not everthing went smoothly:

  • Licensing: I wanted to use the official name Othello, so I had to negotiate with Megahouse. We reached a deal on a reasonable licensing fee (10% after Apple’s cut), but I was not able to get them to agree to any business model other than a fixed paid-up-front price for the app.
  • iAd: Apple announcing the end of iAd didn’t help. So I was stuck with a paid up-front app for the first year; I figured I could re-negotiate after that (which I did, more below). However, not being free means the installed base becomes much smaller, thus Game Center play would not work as well (there may not always be a player available when you’re looking for a match).
  • Game Center: Implementing Game Center support was a mixed bag. Achievements and leaderboards were easy, but turn-based game play took a lot longer than expected, due to poor documentation, bugs, and APIs that are not fully baked. I could have implemented my own server in the time I spent getting Game Center play to work.

Release

Those obstacles slowed me down, but didn’t stop me. (Maybe they should have.) I finally got SmartOthello released on August 15, 2016, localized into Japanese, German, and French. In my unbiased opinion, I think design, game play, and usability are the best of any Othello app on the App Store.

Sales were underwhelming. Some possible contributing factors:

  • Press: I had created a press kit and reached out to press, but didn’t manage to get much coverage. August may not have been the best time to launch.
  • Go Players: I expected to be able to get some of my Go customers interested in Othello, but turns out there’s little crossover interest between the two games.
  • Othello players: My contacts in the Othello community were not as relevant as I thought, for several reasons:
    (1) The community of serious Othello players is very small.
    (2) There are some strong Othello analysis apps on Android, and thus most strong players are using Android, not iOS.
    (3) The core Othello community doesn’t really connect much with the general game-playing public, which is my audience.
  • Megahouse: I had expected Megahouse to help promote the app in Japan, a major Othello-playing country. Nothing.
  • Unlicensed apps: Lots of apps in the App Store continue to use the name Othello without being properly licensed. Megahouse has had little success getting those apps removed.

So the launch was not perfect. But it got worse.

iOS 10

  • At WWDC 2016, Apple announced that the Game Center app was going away, and that Game Center match-making was going through iMessage. This was ominous, but at least during the beta period, things worked reasonably well.
  • When iOS 10 was released in September, the avatar pictures disappeared. As the design of SmartOthello was heavily based around the avatars, I had to create my own bandaid for that.
  • Together with the cumbersome match-making using iMessage, I think the loss of the avatars was the death-knell for Game Center. (It‘s still technically alive, but without any improvements in iOS 11, it‘s not the technology to bet on.)
  • I had heard people complain about App Store search for years, but my Go apps always ranked reasonably well in search. With SmartOthello, search was a clear problem. Adding a space to change the app name to Smart Othello actually helped a bit, which is ridiculous. (The licensing restriction of not being able to include Reversi in the name also hurt.)
  • Since SmartOthello was a $2.99 app, I could at least benefit from Search Ads, but not enough for the app to get real traction.

Advertising and sale

  • In November 2016, I got the chance to advertise at the Othello World Championship in Japan. The resulting bump in sales? Minuscule. Again, it shows a disconnect between the serious player community and the general public I’m trying to reach.
  • I got permission from Megahouse to run a $0.99 sale over the holidays. More sales, slightly less profit. Back to $2.99.

Free with IAP and ads

  • By April 2017, I got the agreement with Megahouse renegotiated, allowing me to experiment with different business models and prices. So I tried free with ads and in-app purchase.
  • That experiment failed miserably. Sales went from low to near zero. While usage of the app went way up, I was not able to get the number of downloads that would be needed for the ads to generate significant revenue, and too few users upgraded to the Pro version. (My ads may have been too nice, and I may have included too much in the free version, but without more downloads, experimenting with those parameters would be futile.)
  • It’s possible this business model might have worked better when I first released the app and it got its initial attention; it’s much harder to generate interest with a new business model than with a new app.

Conclusions

  • Swift worked out really well. I’m very happy with the Swift foundation I got from the Othello project, and the conversion of my Go apps to Swift is continuing.
  • I’m glad I figured out issues with Game Center before trying to integrate that into my Go apps. At least that disaster was averted.
  • From an App Store perspective, Othello is more different from Go than I expected. The audience seems to consist more of casual players rather than people interested in a specific game, and as such they are probably less willing to spend money on the game. Thus my experience with Go was not as valuable; also, any conclusions based on this Othello experience might not transfer back to Go.

Future

I still love how the SmartOthello app turned out, and I will leave it in the App Store, giving it a chance to get noticed and grow over time. There’s much more I could do with it, but I can’t afford to invest more development effort into it at this point. Major changes (such as replacing Game Center, for instance) will have to wait until I get the Go apps converted to Swift and updated for iOS 11, get Go Books available on other platforms, and more: it will be a while.

SmartOthello will go back to paid shortly. Grab it for free while you can.

Moves to Unique Game

The Ke Jie vs. AlphaGo games quickly reached a position that was not in the GoGoD game collection of almost 90,000 professional game records: Game 1 was unique at move 5, game 2 was unique at move 7. To me, this seemed very early, and @badukaire on Twitter got me to wonder: How soon does a pro game usually reach a position that’s different from any previously played game?

Number of moves to unique game

Time for some data: I ran SmartGo’s fuseki matching on the whole GoGoD game collection (excluding handicap games). In that data set, the highest probability for a move to become unique is at move 8; the median is between move 11 and 12; the average is about move 13. Games are unique by move 7 in about 16% of games; by move 5 in only about 4%.

So it’s somewhat unusual to diverge from standard play that early, but there’s more variety of play early in the game than I expected. Also, I’m sure that a lot of games will soon be copying those moves by AlphaGo and Ke Jie, and those opening moves will be unique no more.

Search and URL Scheme

The newest versions of SmartGo Kifu and SmartGo for Macintosh both include the enhanced names dictionary by John Fairbairn (GoGoD), with mini-biographies of over 4,000 players: life, career, status, teacher, Go style, and notes. Just tap on the player name above the board to see the biography.

Blog takemiya bio 

Improved search

The names dictionary includes translations as well as alternate names, and these are now used to significantly improve searching for players. Just type in the search bar, and it will try to match any property containing that text.

You can use ! to negate, e.g. type ‘Kato !Masao’ to look for all the other players named Kato. Anything that looks like a four-digit year will be matched to the date property, and you can search for a range, so e.g. ‘1990-1994 Takemiya’ will search for games Takemiya played during those years.

For more precise searches, you can test for specific properties and conditions, and combine conditions using & (and) and | (or). For example, you can type ‘winner=Lee Sedol & result~~0.5’ to find half-point wins by Lee Sedol (spelled Yi Se-tol in the game collection).

Blog winner lee sedol 

URL scheme

This kind of search is powerful within the app, but you can now access it from other apps too, thanks to the smartgo:// URL scheme. For example, the following link will get you directly to Shusaku’s ear-reddening move:

smartgo://games?id=1846-09-11a#127

Or find all the games played between AlphaGo and Ke Jie:

smartgo://games?player==AlphaGo & player==Ke Jie

Or find cool kyu-level problems:

smartgo://problems?coolness=10 & difficulty<=1k

Recent games of Gu Li playing black against Lee Sedol:

black==Gu Li & white==Lee Sedol & date>=2012

Games that Takemiya won by resignation playing black against a 9 dan:

smartgo://games?black=Takemiya Masaki & result=B+R & rankw=9d

Games played in the Kisei or Honinbo tournaments:

smartgo://games?event~~Kisei | event~~Honinbo

Three-stone handicap games played in the ’90s:

smartgo://games?handicap=3 & date>=1990 & date<=1999

Single-digit kyu life and death problems:

smartgo://problems?difficulty<=1k & difficulty>=9k & genre~~life

Please let me know how you use this new feature, and what could make it more useful to you.

Properties and operators

Here’s the complete list of properties currently supported (SGF tag):

  • Player: player (PB/PW), black (PB), white (PW), winner (PB/PW/RE), loser (PB/PW/RE), rankb (BR), rankw (WR).
  • Game info: id (GN), date (DT), event (EV), round (RO), komi (KM), handicap (HA), oldhandicap (OH), result (RE), rules (RU), time (TM), source (SO), analysis (AN), user (US), comment (GC).
  • Problems: difficulty (DI), coolness (CO), genre (GE).
  • Special: favorite (FA), any (any game info property).

The following operators are supported (comparisons are not case sensitive):

  • == or = : Equal
  • != : Not equal
  • ^= : Starts with
  • ~~ : Contains
  • !~ : Does not contain
  • >= : At least
  • <= : At most