For quite some time I've been looking into building a programming language that is both functional and supports lazy evaluation. I may not have the energy to actually write the code, but I do keep thinking about it.
First thing I should have realized earlier: studying the theoretical foundations of functional languages (ie. The lambda calculus) would help me achieve my goals.
First, what is lazy evaluation? Essentially, an expression is only evaluated as long as the calculations lead to the final result. In a Haskell-like fictional language, I would say function "Pi" calculates Pi to an infinite number of digits. Then "take 100 Pi" would take the first 100 digits of Pi and stop computing any further digits.
My initial mental model of this process was very convoluted. Essentially, requiring back-tracking on the stack. Now, looking at the lambda calculus, it makes a lot more sense.
Recall that the lambda calculus consists of a series of variables and production rules. Rules are of the form: \xy.yx, where \ is a make-shift lambda, xy are the input, and yx the output. Essentially flipping the values.
Execution in a functional language starts with a function. Let's call it R. Well, R does nothing on its own, so let's evaluate it. We get T 3 P. Yup, same as the pseudo-Haskell above, except 3 digits. Let's try parsing this.
First, some syntax. : means element in a list. So x:P means x is the first element from P...
I expand it to something like:
(a:b:c.P)
At each step in T n, we take the head of the list from P and recursively call T (n-1). In the end, we are matching a set of 3 elements to the infinite list P. Notice that once P evaluates 3 elements, the matching is complete and any further evaluation of P is not needed.
This is what escaped me! That means structured properly I could evaluate and match expressions and get lazy evaluation for free! Or so I believe. I'll keep on reflecting on the corner cases, but for now "early match and discard" seems to do the trick.
Thursday, October 10, 2013
Saturday, June 22, 2013
The Future Rants
Glimpses of a potential future have been assembled in the past two weeks thanks to leaks that media companies were happy to publish as infotainment. Interests will wane, and like all other hot topics buried and people will omit what is happening from their collective consciousness unless people feed the fire by making more of a scene from the information.
Arguments for keeping track of everything seem to revolve around preventing people from doing bad things. If the good guys of enforcement have access to all communications then they can prevent bad things from even landing on the radar. Looking at patterns, anomalies can be spotted and investigated. From the anomalies, bad people can be exposed.
Perfect people would manage this information tracking system so it would only be used for its intended purposes. Perfect people would make the perfect hardware and software setup so it can't be hacked. Perfect people would peruse the data looking for information without thinking of personal gain. Perfect people would take utmost care of the data so no outside person sees it. Perfect algorithms to sift through the data. Perfect data to identify the right person. Complete and utter perfection now and indefinitely into the future.
People aren't perfect. The world isn't split into good and evil. Good people win since the winners decide what is good -- play on history is written by the winners.
There's enough information floating on the Internet, that if someone were to capture it, for a single person they could determine income, circle of friends, health status/records, interests, means of expression, opinions, every purchase ever done, current location, affiliations, etc. A complete profile, perfect for nefarious purposes.
Now I get off my soap box. This is probably a repetition of what others said. I just needed to collect my thoughts a bit and reflect.
Tuesday, December 27, 2011
Classifiers for mobile devices
Imagine, for some reason, someone wanted to have on a mobile device a classifier that could recognize various object classes. That is, if the system recognized face it would not be able to tell to who it belonged - but it would know that it was a face. Viola and Jones provide a nice system (Haar-like classifiers) to do such classification for faces. For anyone who wants to go further, Eigenfaces are a good place to start looking.
What I ask is: is there a way to rig a classifier based on Haar-like features to classify multiple features in parallel? That is, can we efficiently detect 50+ object classes on an iPod/iPad in real time? (I like developing on iOS, I know Android is more "open", but the Apple's tools are a pleasure to use!)
Derivatives are the solution. I forget in which paper I read this, but the rectangular features that are subtracted from each other are like coarse derivatives using many pixel values for normalization purposes. This is achieved by realizing that the Haar-like features form a basis that is over-represented (it's not orthonormal).
So - in other words the Haar-like features present a reduced/compressed form of the image which is convenient for recognition purposes.
According to my arithmetic, at lower capture resolutions, 10 passes over the image have to be done to scan a 24x24 window over the image (one pass per scale). So we start with a 24x24 window that is slide across the entire image, then we increase it's size to say 28x28 pixels and slide it across the image, and so forth. According to Viola and Jones the increment should percentage of the original size (as is the shifting). I really recommend reading their paper: Rapid Object Detection using a Boosted Cascade of Simple Features.
What I started to ask myself: could a Haar-like feature be shared among multiple classes of objects? Yes. The features are differences. The varying thresholds to accept a feature may pose a problem, but that would be solved with how the data is organized.
But I have a bet. An interesting bet. If we have just a horizontal and vertical feature. Could we restrict it to a square? Yes... That means that the first feature we pick is going to be a square (either horizontal or vertical split for the subtraction). It also means that we have divided the space into 2 buckets.
Ok, next round - what is the standard deviation among the features from the training set? That should determine the next round of features that will be placed spatially around the initial feature. So we detect a rectangle, but that rectangle may belong to a larger feature which we must then scan for by checking surrounding rectangles.
However, we can not let the number of rectangles to test explode after the initial round.
By using this trick, Adaboost in the form that Viola and Jones describes would not be sufficient. Rather, I believe rigging the features so they don't overlap may provide the solution that I need.
This is as far as my thinking has gone (except for a means of accelerating the training so it could be done on the iOS device as well - I've solved it and managed to find a way to reduce storage needs, but that is the simple part of the puzzle that I'm looking at).
What I ask is: is there a way to rig a classifier based on Haar-like features to classify multiple features in parallel? That is, can we efficiently detect 50+ object classes on an iPod/iPad in real time? (I like developing on iOS, I know Android is more "open", but the Apple's tools are a pleasure to use!)
Derivatives are the solution. I forget in which paper I read this, but the rectangular features that are subtracted from each other are like coarse derivatives using many pixel values for normalization purposes. This is achieved by realizing that the Haar-like features form a basis that is over-represented (it's not orthonormal).
So - in other words the Haar-like features present a reduced/compressed form of the image which is convenient for recognition purposes.
According to my arithmetic, at lower capture resolutions, 10 passes over the image have to be done to scan a 24x24 window over the image (one pass per scale). So we start with a 24x24 window that is slide across the entire image, then we increase it's size to say 28x28 pixels and slide it across the image, and so forth. According to Viola and Jones the increment should percentage of the original size (as is the shifting). I really recommend reading their paper: Rapid Object Detection using a Boosted Cascade of Simple Features.
What I started to ask myself: could a Haar-like feature be shared among multiple classes of objects? Yes. The features are differences. The varying thresholds to accept a feature may pose a problem, but that would be solved with how the data is organized.
But I have a bet. An interesting bet. If we have just a horizontal and vertical feature. Could we restrict it to a square? Yes... That means that the first feature we pick is going to be a square (either horizontal or vertical split for the subtraction). It also means that we have divided the space into 2 buckets.
Ok, next round - what is the standard deviation among the features from the training set? That should determine the next round of features that will be placed spatially around the initial feature. So we detect a rectangle, but that rectangle may belong to a larger feature which we must then scan for by checking surrounding rectangles.
However, we can not let the number of rectangles to test explode after the initial round.
By using this trick, Adaboost in the form that Viola and Jones describes would not be sufficient. Rather, I believe rigging the features so they don't overlap may provide the solution that I need.
This is as far as my thinking has gone (except for a means of accelerating the training so it could be done on the iOS device as well - I've solved it and managed to find a way to reduce storage needs, but that is the simple part of the puzzle that I'm looking at).
Sunday, November 27, 2011
Haskell "to add or multiply" (slow version)
Today I'm rewriting this entire post (minus the code - it's very ugly code in retrospect). So I'm trying to solve the "to add or multiply" problem from the ACM 2011 finals. My hunch last week was that it could be solved in linear time. This week I explored what I thought would be a linear solution and came back with some interesting notes.
Before I jump into the subject; the problem gives two ranges of integers (start and end), and two constants 'a' and 'm'. Find arbitrary positive integers [i1, i2, i3, ... in] such that the following is contained within the range 'end':
(((start+i1*a)*m^i2 + i3*a)*m^i4 + i5*a) ....
(where arithmetic on ranges is identical to operations on a 2-vector. a range consists of a start-value and end-value)
First, I now believe that there is no linear-time solution. Intuitively, the reason is because there is a destination range and not a destination number.
Consider the inclusive range [a,b]. Now, there's a number 'x' that I can multiply an integer 'j' to get a value between a and b. I also want 'j' to be minimal. This is quite simple - if 'a mod x = 0' then the answer is a/x else it is 1+a/x. Recall the computer always floors integer data, we would want the ceiling.
Within the problem, there's a point at which both j=a/x and j=1+a/x should be explored. Below I'll try to informally explain this:
For the problem, it is possible to find a value 'a' that is the maximum number of multiplications that may be applied to [p,q] (the start range) until the values exceed [r,s] or the number of integers within the range of [p,q] exceeds that of [r,s].
The maximum number of multiplications is invariant no matter how many addition operators are present. Simple example ('x' is a starting value, 'a' and 'm' are integers):
(x+a)m = xm + am
'am' will shift values a constant amount regardless of the starting value 'x'. This is important, look at a range [2,3] -- [2,3] has 3 digits, [2,3]*4 = [8,12] has 5 digits. We can add integers before the multiplication and the number of digits will not change. This allows us to compute another value -- the minimum number of additions needed until a solution.
If it is not possible to find a series of additions that satisfies the start range and end range, then we can not conclude that there is no solution. Consider that the increments of addition is 100,000 and multiplication is 2 with start range [1,2] to [5,20].
Neither is it possible to rely solely on multiplication. Consider addition of 1 and multiplication of 7, start range [1,2] and end range [14,21]. The following is contained within the range: ([1,2]+1)*7
Knowing these two values helps us when searching for a solution. The solution becomes:
x*m^j + i1*a*m^0 + i2*a*m^1 + ...
It's a matter of finding i1, i2, i... (j-1 unknowns). If multiplication is 1 or 0, or if addition is 0 then the solution can be directly computed in constant time (depending upon your implementation of log - or if you decide to loop over values for multiplication -- which makes it O(n)).
Anyhow, a good strategy is to attempt to maximize the i for the m with the largest exponents. Think of this as a heuristic. When the number of integers can be in the tens of thousands, doing a brute-force search will be slow and memory consuming (simple arithmetic, just it will look like a mess here).
I'll continue playing with the numbers, maybe something interesting will pop out.
For historical purposes, here's a very slow / bad implementation of "to add or multiply":
import Data.Monoid
import Data.Char
-- AddOrMultiply
-- Given the ability to add 'a' or multiply 'm', see if there is a sequence
-- that starts in range [p,q] and ends in range [r,s]
data Operation = A Integer | M Integer
instance Show (Operation) where
show (A v) = " " ++ (show v) ++ "A"
show (M v) = " " ++ (show v) ++ "M"
apply :: Operation -> Integer -> Integer
apply (A v) a = (v+a)
apply (M v) a = (v*a)
data Partial = Partial (Integer,Integer,Integer) [Operation] deriving (Show)
applyl :: (Integer,Integer) -> [Operation] -> Partial
applyl (min,max) os = Partial (foldr apply min os,foldr apply max os,0) os
incrementMul :: Operation -> Integer -> Integer
incrementMul (M m) v = 1
incrementMul _ v = v
combineOps :: Operation -> [Operation] -> [Operation]
combineOps o [] = [o]
combineOps (A a) ((A as):xs) = (A $ a+as):xs
combineOps (M m) ((M ms):xs) = (M $ m+ms):xs
combineOps o xs = o:xs
add :: Operation -> Partial -> Partial
add o (Partial (min,max,mul) os) = Partial (apply o min, apply o max, incrementMul o mul) $ combineOps o os
validAdd :: (Integer,Integer)->Partial -> Bool
validAdd _ (Partial (_,_,0) _) = True
validAdd (a,m) (Partial _ ((A a1):_)) = a1 < (a*m)
validAdd _ _ = True
valid :: (Integer,Integer) -> (Integer,Integer) -> Partial -> Bool
valid am (r,s) (Partial (min,max,mul) os) = if (max <= s && max-min <= s-r && (validAdd am $ Partial (min,max,mul) os)) then True else False
iteration :: (Integer,Integer) -> (Integer,Integer) -> Partial -> [Partial]
iteration (a,m) rs ptl = filter (valid (a,m) rs) [add (A a) ptl, add (M m) ptl]
-- concatMap :: (a -> [b]) -> [a] -> [b]
startCondition :: (Integer,Integer) -> [Partial]
startCondition (p,q) = [Partial (p,q,0) []]
success :: (Integer,Integer) -> Partial -> Bool
success (r,s) (Partial (min,max,_) _) = if (min >= r && max <= s) then True else False
iterationl :: (Integer,Integer) -> (Integer,Integer) -> [Partial] -> [Partial]
iterationl (a,m) rs ps = concatMap (iteration (a,m) rs) ps
everything :: (Integer,Integer) -> (Integer,Integer) -> [Partial] -> [Partial]
everything _ _ [] = []
everything am rs ps = ps ++ (everything am rs $ iterationl am rs ps)
solutions :: (Integer,Integer) -> (Integer,Integer) -> (Integer,Integer) -> [Partial]
solutions am pq rs = filter (success rs) $ everything am rs $ startCondition pq
type Ampqrs = (Integer,Integer,Integer,Integer,Integer,Integer)
reformat :: (Integer,Integer) -> [Operation] -> [Operation]
reformat _ [] = []
reformat am ((A a1):(A a2):xs) = reformat am $ (A $ a1+a2):xs
reformat am ((M m1):(M m2):xs) = reformat am $ (M $ m1+m2):xs
reformat (a,m) ((A a1):xs) = ((A $ a1 `div` a):(reformat (a,m) xs))
reformat (a,m) ((M m1):xs) = ((M $ m1 `div` m):(reformat (a,m) xs))
solutionPartial :: Ampqrs -> [Partial]
solutionPartial (a,m,p,q,r,s) = take 1 $ solutions (a,m) (p,q) (r,s)
operationFromPartial :: Partial -> [Operation]
operationFromPartial (Partial _ o) = o
solution' :: (Integer,Integer) -> [Partial] -> Maybe [Operation]
solution' _ [] = Nothing
solution' am xs = Just . reverse . (reformat am) . operationFromPartial . head $ xs
solutionOp :: Ampqrs -> Maybe [Operation]
solutionOp (a,m,p,q,r,s) = solution' (a,m) $ solutionPartial (a,m,p,q,r,s)
solutionOp' :: [String] -> Maybe [Operation]
solutionOp' [a,m,p,q,r,s] = solutionOp (read a, read m, read p, read q, read r, read s)
parseLine :: String -> Maybe [Operation]
parseLine s = solutionOp' $ words s
display :: Integer -> [String] -> String
display _ [] = []
display _ [a] = []
display i (x:xs) = ("Case " ++ (show i) ++ ": " ++ (show $ parseLine x)) ++ ['\n'] ++ (display (i+1) xs)
main = do
contents <- getContents
putStr . (display 1) $ lines contents
Before I jump into the subject; the problem gives two ranges of integers (start and end), and two constants 'a' and 'm'. Find arbitrary positive integers [i1, i2, i3, ... in] such that the following is contained within the range 'end':
(((start+i1*a)*m^i2 + i3*a)*m^i4 + i5*a) ....
(where arithmetic on ranges is identical to operations on a 2-vector. a range consists of a start-value and end-value)
First, I now believe that there is no linear-time solution. Intuitively, the reason is because there is a destination range and not a destination number.
Consider the inclusive range [a,b]. Now, there's a number 'x' that I can multiply an integer 'j' to get a value between a and b. I also want 'j' to be minimal. This is quite simple - if 'a mod x = 0' then the answer is a/x else it is 1+a/x. Recall the computer always floors integer data, we would want the ceiling.
Within the problem, there's a point at which both j=a/x and j=1+a/x should be explored. Below I'll try to informally explain this:
For the problem, it is possible to find a value 'a' that is the maximum number of multiplications that may be applied to [p,q] (the start range) until the values exceed [r,s] or the number of integers within the range of [p,q] exceeds that of [r,s].
The maximum number of multiplications is invariant no matter how many addition operators are present. Simple example ('x' is a starting value, 'a' and 'm' are integers):
(x+a)m = xm + am
'am' will shift values a constant amount regardless of the starting value 'x'. This is important, look at a range [2,3] -- [2,3] has 3 digits, [2,3]*4 = [8,12] has 5 digits. We can add integers before the multiplication and the number of digits will not change. This allows us to compute another value -- the minimum number of additions needed until a solution.
If it is not possible to find a series of additions that satisfies the start range and end range, then we can not conclude that there is no solution. Consider that the increments of addition is 100,000 and multiplication is 2 with start range [1,2] to [5,20].
Neither is it possible to rely solely on multiplication. Consider addition of 1 and multiplication of 7, start range [1,2] and end range [14,21]. The following is contained within the range: ([1,2]+1)*7
Knowing these two values helps us when searching for a solution. The solution becomes:
x*m^j + i1*a*m^0 + i2*a*m^1 + ...
It's a matter of finding i1, i2, i... (j-1 unknowns). If multiplication is 1 or 0, or if addition is 0 then the solution can be directly computed in constant time (depending upon your implementation of log - or if you decide to loop over values for multiplication -- which makes it O(n)).
Anyhow, a good strategy is to attempt to maximize the i for the m with the largest exponents. Think of this as a heuristic. When the number of integers can be in the tens of thousands, doing a brute-force search will be slow and memory consuming (simple arithmetic, just it will look like a mess here).
I'll continue playing with the numbers, maybe something interesting will pop out.
For historical purposes, here's a very slow / bad implementation of "to add or multiply":
import Data.Monoid
import Data.Char
-- AddOrMultiply
-- Given the ability to add 'a' or multiply 'm', see if there is a sequence
-- that starts in range [p,q] and ends in range [r,s]
data Operation = A Integer | M Integer
instance Show (Operation) where
show (A v) = " " ++ (show v) ++ "A"
show (M v) = " " ++ (show v) ++ "M"
apply :: Operation -> Integer -> Integer
apply (A v) a = (v+a)
apply (M v) a = (v*a)
data Partial = Partial (Integer,Integer,Integer) [Operation] deriving (Show)
applyl :: (Integer,Integer) -> [Operation] -> Partial
applyl (min,max) os = Partial (foldr apply min os,foldr apply max os,0) os
incrementMul :: Operation -> Integer -> Integer
incrementMul (M m) v = 1
incrementMul _ v = v
combineOps :: Operation -> [Operation] -> [Operation]
combineOps o [] = [o]
combineOps (A a) ((A as):xs) = (A $ a+as):xs
combineOps (M m) ((M ms):xs) = (M $ m+ms):xs
combineOps o xs = o:xs
add :: Operation -> Partial -> Partial
add o (Partial (min,max,mul) os) = Partial (apply o min, apply o max, incrementMul o mul) $ combineOps o os
validAdd :: (Integer,Integer)->Partial -> Bool
validAdd _ (Partial (_,_,0) _) = True
validAdd (a,m) (Partial _ ((A a1):_)) = a1 < (a*m)
validAdd _ _ = True
valid :: (Integer,Integer) -> (Integer,Integer) -> Partial -> Bool
valid am (r,s) (Partial (min,max,mul) os) = if (max <= s && max-min <= s-r && (validAdd am $ Partial (min,max,mul) os)) then True else False
iteration :: (Integer,Integer) -> (Integer,Integer) -> Partial -> [Partial]
iteration (a,m) rs ptl = filter (valid (a,m) rs) [add (A a) ptl, add (M m) ptl]
-- concatMap :: (a -> [b]) -> [a] -> [b]
startCondition :: (Integer,Integer) -> [Partial]
startCondition (p,q) = [Partial (p,q,0) []]
success :: (Integer,Integer) -> Partial -> Bool
success (r,s) (Partial (min,max,_) _) = if (min >= r && max <= s) then True else False
iterationl :: (Integer,Integer) -> (Integer,Integer) -> [Partial] -> [Partial]
iterationl (a,m) rs ps = concatMap (iteration (a,m) rs) ps
everything :: (Integer,Integer) -> (Integer,Integer) -> [Partial] -> [Partial]
everything _ _ [] = []
everything am rs ps = ps ++ (everything am rs $ iterationl am rs ps)
solutions :: (Integer,Integer) -> (Integer,Integer) -> (Integer,Integer) -> [Partial]
solutions am pq rs = filter (success rs) $ everything am rs $ startCondition pq
type Ampqrs = (Integer,Integer,Integer,Integer,Integer,Integer)
reformat :: (Integer,Integer) -> [Operation] -> [Operation]
reformat _ [] = []
reformat am ((A a1):(A a2):xs) = reformat am $ (A $ a1+a2):xs
reformat am ((M m1):(M m2):xs) = reformat am $ (M $ m1+m2):xs
reformat (a,m) ((A a1):xs) = ((A $ a1 `div` a):(reformat (a,m) xs))
reformat (a,m) ((M m1):xs) = ((M $ m1 `div` m):(reformat (a,m) xs))
solutionPartial :: Ampqrs -> [Partial]
solutionPartial (a,m,p,q,r,s) = take 1 $ solutions (a,m) (p,q) (r,s)
operationFromPartial :: Partial -> [Operation]
operationFromPartial (Partial _ o) = o
solution' :: (Integer,Integer) -> [Partial] -> Maybe [Operation]
solution' _ [] = Nothing
solution' am xs = Just . reverse . (reformat am) . operationFromPartial . head $ xs
solutionOp :: Ampqrs -> Maybe [Operation]
solutionOp (a,m,p,q,r,s) = solution' (a,m) $ solutionPartial (a,m,p,q,r,s)
solutionOp' :: [String] -> Maybe [Operation]
solutionOp' [a,m,p,q,r,s] = solutionOp (read a, read m, read p, read q, read r, read s)
parseLine :: String -> Maybe [Operation]
parseLine s = solutionOp' $ words s
display :: Integer -> [String] -> String
display _ [] = []
display _ [a] = []
display i (x:xs) = ("Case " ++ (show i) ++ ": " ++ (show $ parseLine x)) ++ ['\n'] ++ (display (i+1) xs)
main = do
contents <- getContents
putStr . (display 1) $ lines contents
Sunday, September 4, 2011
Sonic 4 EP 1 iOS Impressions
The price for Sonic 4 EP 1 has invariably dropped following the trend of most applications on iOS. Sell for the maximum that the consumer will allow then drop to catch the cheapskates. Others take the route of giving away a base application (or charging at probably a loss) and recuperating on paid extensions.
First, this game has 17 levels + special stages for each. Ignoring the boss-fights and the shorter levels, that's about 11 levels. Let me put the price of this game in perspective ($4.99) for 4 zones and 11 long levels (disclaimer, I didn't research anything about the profit margins of this game). The first sonic game had about 8 zones, or 24 levels. Just shy of double what this game offers. And for much more than double the cost. My point: people who say it's worth ($0.99) ... wake up and look at the development effort! The levels appear to be huge + multiple sound-tracks -- and testing such levels is no small feat. The two previous published iOS games I worked on would need to be more than ($0.99) to recuperate the investment given a target market. (For those saying it appeared on multiple consoles -- it's not up to the other machines to finance the development for the iOS version).
For those complaining about bugs / crashing. Email SEGA. Complaining on the Apple store ratings page might not help. I had problems with Civ. Emailing (convoluted process) Firaxis gave me the answer I was seeking.
Most of the game reviews already cover every aspect of the game. I shouldn't need to go into more details there.
What I'm about to rant about is that it's a cross-platform game. A game designed for game consoles as well as iOS. And that's the main fault I find with it.
The developers (to save time) probably (I'm guessing here) had separated the game from everything else. All platforms support getting input. It is the semantics of accessing the input device that changes for each platform. XBox will go through XNA (there might have other options), Wii through (who knows what, it's proprietary), iOS through UIKit. (Graphics, file access, used meshes, etc. may vary depending upon the platform but I'd guess that the game itself is portable C/C++/(other?) and what changes is glue that makes the whole thing run.)
So, you have a game. It wants left-press, right-press, up-press, down-press, and action. That's it. That's how multiple input schemes become possible. Don't like pressing the virtual D-Pad on the iOS device? Then use the tilt sensor and swipe! A myriad of other options could be presented with little effort (normally inundating the user with frivolous options is a bad idea -- power users may like it but they aren't representative of the whole however vocal they may be).
This works perfectly on the systems with physical controls. The Wii, XBox, and PS3. This fails on iOS (not miserably, but it's an issue). If you don't realize that they are just mapping controls, confusion may ensue.
Why confusion? Let's take a special stage for example. They say tilt left and right to turn the world. That's confusing, but it's part of the game. Where it fails is -- wait if I'm holding the iOS device parallel to a table? Should sonic act like a ball in a labyrinth game and slow down? That might actually be more intuitive. Further still, why rotate the world when the whole device can be rotated? The game was programmed for left/right, it gets left/right from tilting left and right. (I've done some testing with accelerometer controls, my conclusion being that they are the hardest to do right)
Jumping is also a bit jarring. The button is statically placed. It's a region. It might be easier if the region covered the whole right-side of the screen.
Moving is also difficult with the D-Pad. To conserve screen-space, it's on the left-hand side. Pressing right can be done by pressing from the end of the go-left button to the middle of the screen (from basic testing). I couldn't feel a neutral position (this is probably just me complaining for nothing).
Later on there are cannons to shoot sonic to a specific spot. It may make more sense to tap in the direction the cannon should face, but it's again press left/right to turn and action to shoot.
Game-wise, Sonic should be on the left of the screen when moving right and vice-versa. This gives the player the ability to anticipate what will happen when running (memorizing the level is fun and all...)
Actually, I had fun playing through the first half of the game. It's not a bad game, they mapped controls in a very coarse way which takes away from the game a bit. And I don't complain given the price, the older games cost much more even after release.
What I want to bring to light are the challenges of cross-platform development. The game was perfectly abstracted away to work on the traditional consoles. iOS is a different beast. Certain changes might actually be too drastic and require too much reworking of the levels (the special zones are difficult since Sonic has a continual pull of gravity in a given direction. Removing that may require rethinking of the levels. It's more nostalgic this way though!) You might want to aim the cannon towards one of the on-screen buttons, etc.
First, this game has 17 levels + special stages for each. Ignoring the boss-fights and the shorter levels, that's about 11 levels. Let me put the price of this game in perspective ($4.99) for 4 zones and 11 long levels (disclaimer, I didn't research anything about the profit margins of this game). The first sonic game had about 8 zones, or 24 levels. Just shy of double what this game offers. And for much more than double the cost. My point: people who say it's worth ($0.99) ... wake up and look at the development effort! The levels appear to be huge + multiple sound-tracks -- and testing such levels is no small feat. The two previous published iOS games I worked on would need to be more than ($0.99) to recuperate the investment given a target market. (For those saying it appeared on multiple consoles -- it's not up to the other machines to finance the development for the iOS version).
For those complaining about bugs / crashing. Email SEGA. Complaining on the Apple store ratings page might not help. I had problems with Civ. Emailing (convoluted process) Firaxis gave me the answer I was seeking.
Most of the game reviews already cover every aspect of the game. I shouldn't need to go into more details there.
What I'm about to rant about is that it's a cross-platform game. A game designed for game consoles as well as iOS. And that's the main fault I find with it.
The developers (to save time) probably (I'm guessing here) had separated the game from everything else. All platforms support getting input. It is the semantics of accessing the input device that changes for each platform. XBox will go through XNA (there might have other options), Wii through (who knows what, it's proprietary), iOS through UIKit. (Graphics, file access, used meshes, etc. may vary depending upon the platform but I'd guess that the game itself is portable C/C++/(other?) and what changes is glue that makes the whole thing run.)
So, you have a game. It wants left-press, right-press, up-press, down-press, and action. That's it. That's how multiple input schemes become possible. Don't like pressing the virtual D-Pad on the iOS device? Then use the tilt sensor and swipe! A myriad of other options could be presented with little effort (normally inundating the user with frivolous options is a bad idea -- power users may like it but they aren't representative of the whole however vocal they may be).
This works perfectly on the systems with physical controls. The Wii, XBox, and PS3. This fails on iOS (not miserably, but it's an issue). If you don't realize that they are just mapping controls, confusion may ensue.
Why confusion? Let's take a special stage for example. They say tilt left and right to turn the world. That's confusing, but it's part of the game. Where it fails is -- wait if I'm holding the iOS device parallel to a table? Should sonic act like a ball in a labyrinth game and slow down? That might actually be more intuitive. Further still, why rotate the world when the whole device can be rotated? The game was programmed for left/right, it gets left/right from tilting left and right. (I've done some testing with accelerometer controls, my conclusion being that they are the hardest to do right)
Jumping is also a bit jarring. The button is statically placed. It's a region. It might be easier if the region covered the whole right-side of the screen.
Moving is also difficult with the D-Pad. To conserve screen-space, it's on the left-hand side. Pressing right can be done by pressing from the end of the go-left button to the middle of the screen (from basic testing). I couldn't feel a neutral position (this is probably just me complaining for nothing).
Later on there are cannons to shoot sonic to a specific spot. It may make more sense to tap in the direction the cannon should face, but it's again press left/right to turn and action to shoot.
Game-wise, Sonic should be on the left of the screen when moving right and vice-versa. This gives the player the ability to anticipate what will happen when running (memorizing the level is fun and all...)
Actually, I had fun playing through the first half of the game. It's not a bad game, they mapped controls in a very coarse way which takes away from the game a bit. And I don't complain given the price, the older games cost much more even after release.
What I want to bring to light are the challenges of cross-platform development. The game was perfectly abstracted away to work on the traditional consoles. iOS is a different beast. Certain changes might actually be too drastic and require too much reworking of the levels (the special zones are difficult since Sonic has a continual pull of gravity in a given direction. Removing that may require rethinking of the levels. It's more nostalgic this way though!) You might want to aim the cannon towards one of the on-screen buttons, etc.
Saturday, July 30, 2011
First Day of 10.7
I decided it might be worth updating Mac OS to 10.7. First, for anyone thinking of updating, double-check all of your apps. I was surprised by the number of applications that I had that were PowerPC. I think the machine was spending more time running Rosetta than x86 binaries... The important apps I found newer versions and trudged forward. My scanner will only be usable with an older Power Mac.
So. How does the OS fare? Here are the positive and negative features on a per-app/feature basis in my opinion.
Mail.app
Mail.app's interface update is great! The application is now designed for wider screens. On the far left is the side-bar as it was there before (hidden by default but click on "Afficher" -- I guess that's "Show" in English). Organized like it is, stretch the application and there is plenty of room for the previews and other information. The preferences allow for plenty of customization.
iCal
I like the way it looks. Where Mail.app got a massive functionality upgrade, iCal seems to suffer. The problem is that things normally aren't neatly split up between months. So if I'm trying to schedule something it won't be for a specific day but some time-range. So I want to quickly jump between weeks (if possible see multiple weeks) and not be stuck in groups a day/week/month/year. (the option to scroll a day at a time is silly - it renders the velocity scrolling of the magic mouse useless)
For example, if in the week view it scrolled continuously and could display multiple weeks in it's columnar view I'd be very happy. For the day view, for a sufficiently wide screen multiple months can be seen. Even multiple days. On a small 1024x768 screen the spacing is elegant, however I'd argue there is space for two more monthly calendars in day and week view.
Finder
Overall, it's as usable as ever. I can't complain, it feels normal. A few settings I had to re-enable, but it's as I expected. The overall view of all my files is... pointless. It seems to be randomly picking stuff I downloaded -- such as a picture generated by Doxygen.
I'm happy that the library is now hidden. To many things could go wrong by having that exposed to the user. Those of us that wish to muck with system stuff can hit Command-Shift-G. The icons are much clearer.
Terminal
Works. Happy. Why can it go fullscreen?!
LaunchPad
I don't know what to make of this. LaunchPad attempts to present a nicer view of the applications. Sure, that's great! The question that's running around my head is: why isn't LaunchPad's view synchronized with the Application folder?
For example, a folder in Applications becomes a folder in LaunchPad. Of course there are issues with nesting folders but I don't see why folders nested more than one couldn't just be collapsed. Certain applications would be dangerous to move (bad developers)... However organizing my applications once is, in my opinion, preferable.
Actually, I would have loved LaunchPad if it just opened a fancy view of my Applications folder.
Apart from that, it's a great idea. Only if I didn't use the dock to store often-used applications and Spotlight for everything else. (StarCraft II? Spotlight!)
Mission Control
A very good update to Exposé. It unifies all the window management features into one nice spot. One button to see all my windows from all the apps with all the desktops + fullscreen apps. Then, windows from the same application are grouped. It's very nice.
Fullscreen Apps
Really. I wanted to love this. I have a small-ish monitor on the side where I tend to throw documentation, iTunes, Mail, and the web browser. The main monitor is reserved for work (XCode, iOS Simulator, etc.). Never doubt having the documentation on a second monitor!
Fullscreen apps grey out the second monitor. As in, they don't use it! And I can't put floating windows on it if applications on the main monitor are full screen. XCode, which I thought would actually use both monitors (put the Organizer on the second!) spawned the Organizer as a new full-screen window.
Use multiple monitors? You'll go further running the applications in windows.
Safari
The download window is gone and replaced with a pop-up... Thanks Apple!
Unfortunately flash videos skip now when the system is under load...
XCode
The documentation within the Organizer is still a mess. It's more convenient browsing to Apple's site since the side-bar should be synchronized to the documentation (or a viewable side-bar should be there).
No, I don't want Quick Help. Quick Help doesn't even pick up on the Doxygen comments littering my code.
Last Impressions
The Mac OS / iOS hybrid seems to be an odd beast. The ability to quit and resume applications is wonderful. But the addition of fullscreen applications and launchpad feel like kludges. (LaunchPad especially feels like it's tacked on rather than integral).
The scroll-bars... This is minor. Use it for a day or two with the default settings. Within a few minutes I was already used to the scrolling. The elimination of scrollbars didn't affect me: I never used them anyhow. Give it a chance for at least a day.
Autocomplete -- it's annoying when it fails.
Those are my comments from one day with the system...
So. How does the OS fare? Here are the positive and negative features on a per-app/feature basis in my opinion.
Mail.app
Mail.app's interface update is great! The application is now designed for wider screens. On the far left is the side-bar as it was there before (hidden by default but click on "Afficher" -- I guess that's "Show" in English). Organized like it is, stretch the application and there is plenty of room for the previews and other information. The preferences allow for plenty of customization.
iCal
I like the way it looks. Where Mail.app got a massive functionality upgrade, iCal seems to suffer. The problem is that things normally aren't neatly split up between months. So if I'm trying to schedule something it won't be for a specific day but some time-range. So I want to quickly jump between weeks (if possible see multiple weeks) and not be stuck in groups a day/week/month/year. (the option to scroll a day at a time is silly - it renders the velocity scrolling of the magic mouse useless)
For example, if in the week view it scrolled continuously and could display multiple weeks in it's columnar view I'd be very happy. For the day view, for a sufficiently wide screen multiple months can be seen. Even multiple days. On a small 1024x768 screen the spacing is elegant, however I'd argue there is space for two more monthly calendars in day and week view.
Finder
Overall, it's as usable as ever. I can't complain, it feels normal. A few settings I had to re-enable, but it's as I expected. The overall view of all my files is... pointless. It seems to be randomly picking stuff I downloaded -- such as a picture generated by Doxygen.
I'm happy that the library is now hidden. To many things could go wrong by having that exposed to the user. Those of us that wish to muck with system stuff can hit Command-Shift-G. The icons are much clearer.
Terminal
Works. Happy. Why can it go fullscreen?!
LaunchPad
I don't know what to make of this. LaunchPad attempts to present a nicer view of the applications. Sure, that's great! The question that's running around my head is: why isn't LaunchPad's view synchronized with the Application folder?
For example, a folder in Applications becomes a folder in LaunchPad. Of course there are issues with nesting folders but I don't see why folders nested more than one couldn't just be collapsed. Certain applications would be dangerous to move (bad developers)... However organizing my applications once is, in my opinion, preferable.
Actually, I would have loved LaunchPad if it just opened a fancy view of my Applications folder.
Apart from that, it's a great idea. Only if I didn't use the dock to store often-used applications and Spotlight for everything else. (StarCraft II? Spotlight!)
Mission Control
A very good update to Exposé. It unifies all the window management features into one nice spot. One button to see all my windows from all the apps with all the desktops + fullscreen apps. Then, windows from the same application are grouped. It's very nice.
Fullscreen Apps
Really. I wanted to love this. I have a small-ish monitor on the side where I tend to throw documentation, iTunes, Mail, and the web browser. The main monitor is reserved for work (XCode, iOS Simulator, etc.). Never doubt having the documentation on a second monitor!
Fullscreen apps grey out the second monitor. As in, they don't use it! And I can't put floating windows on it if applications on the main monitor are full screen. XCode, which I thought would actually use both monitors (put the Organizer on the second!) spawned the Organizer as a new full-screen window.
Use multiple monitors? You'll go further running the applications in windows.
Safari
The download window is gone and replaced with a pop-up... Thanks Apple!
Unfortunately flash videos skip now when the system is under load...
XCode
The documentation within the Organizer is still a mess. It's more convenient browsing to Apple's site since the side-bar should be synchronized to the documentation (or a viewable side-bar should be there).
No, I don't want Quick Help. Quick Help doesn't even pick up on the Doxygen comments littering my code.
Last Impressions
The Mac OS / iOS hybrid seems to be an odd beast. The ability to quit and resume applications is wonderful. But the addition of fullscreen applications and launchpad feel like kludges. (LaunchPad especially feels like it's tacked on rather than integral).
The scroll-bars... This is minor. Use it for a day or two with the default settings. Within a few minutes I was already used to the scrolling. The elimination of scrollbars didn't affect me: I never used them anyhow. Give it a chance for at least a day.
Autocomplete -- it's annoying when it fails.
Those are my comments from one day with the system...
Friday, July 29, 2011
Software: From Simple to Complicated
A few years ago, I started a small software library. A set of common routines. Seeing how useful it was, I decided to expand it -- make it more general. Apply good software development techniques so that it may prove to be more flexible.
For example, at the beginning I hardcoded the ability to use a single 1024x1024 texture for everything. This meant I had no need to worry about which texture was currently bound (only one) and no need to manage memory (one fixed amount of memory used). Then I expanded this system to load multiple textures using a plist for the parameters.
For the added flexibility, I was able to create textures and use them as objects. Yet; as far as making prototypes go it didn't speed things up. Memory became an issue to manage. And now mipmapping and other little technological ideas for the sake of technology start to creep in when ideas should be driving the technology.
In the end; I should conclude the organization for generic code is only needed when called for. If it works, and works well, why change it?
Subscribe to:
Posts (Atom)