Saturday, January 26, 2008

Scala Hopes And Fears

Main things I want out of Scala:

1. Closures.
2. No checked exceptions.

Main things that scare about Scala:

1. Bloat. One safe-looking line can generate a 30K class file. Helps explain how their own jars are so big.
2. Slow compiler. That they need an 'fsc' ('Fast Scala Compiler') sort of scares me.
3. Creative operator overloading (':::', '/:', '|-', binary '!', and so on since the possibilities are mostly endless).

Monday, January 21, 2008

Scala Pong 4K - Source Code Now

I was afraid to post my source code for Scala Pong 4K because I'm afraid it's not a good example of Scala programming, nor of 4K game programming, nor of programming in general. Learning a new language while staying under 4K with limited time for working on it provides less than ideal results. And I purposely avoided many Scala features because I was afraid of the weight it would add. (4K is really small.) For instance, I avoided arrays. Part of why I chose something as simple as pong (and Pong is really trademarked, so I guess that was a bad name choice). But it makes sense why people might want to see the source anyway. Also, finally, here is the Scala license which says I'm supposed to show their terms and copyright (though it would be nice if ProGuard could have stripped it all out, just to make it smaller).

All that out of the way, here's the source code which I kept all in one file "Game.scala". And it looks like it get visually chopped off on the right, so you might need to copy and paste it into an editor to see everything:

package sp4k

import java.awt._
import java.awt.event._
import javax.swing._

object Game {

// Don't extend a big class with an 'object'. That makes tons of static methods.

def main(args: Array[String]) {
SwingUtilities.invokeAndWait(new GamePanel)
}

}

class GamePanel extends JPanel with Runnable with ActionListener {

// Closure vars are smaller than member vars because no accessor methods are generated.
// Except that then it uses Scala's IntRef class, so a new dependency.
// So back to member vars.

var y1 = 200
var v1 = 0
var y2 = 200
var v2 = 0

var xb = 0
var yb = 0
var vx = 0
var vy = 0

def actionPerformed(e: ActionEvent) {
xb += vx
yb += vy
if (xb < -5 || xb > 605) {
initBall()
} else if (xb > 573) {
if (yb > y2 - 40 && yb < y2 + 40) {
xb = 573
vx = -vx - 1
}
} else if (xb < 27) {
if (yb > y1 - 40 && yb < y1 + 40) {
xb = 27
vx = -vx + 1
}
}
if (yb > 375) {
yb = 375
vy = -vy
} else if (yb < 25) {
yb = 25
vy = -vy
}
y1 += v1
if (y1 < 60) {
y1 = 60
} else if (y1 > 340) {
y1 = 340
}
y2 += v2
if (y2 < 60) {
y2 = 60
} else if (y2 > 340) {
y2 = 340
}
repaint()
}

def bar(g: Graphics2D, x1: Int, y: Int, x2: Int) {
g.setPaint(new GradientPaint(x1, 0, new Color(40, 40, 0), x2, 0, new Color(160, 160, 0)))
g.fillRect(java.lang.Math min(x1, x2), y - 40, java.lang.Math abs(x2 - x1), 80)
}

def initBall() {
xb = 300
yb = 200
vx = (if (java.lang.Math.random() < 0.5) -1 else 1) * ((10 * java.lang.Math.random()).asInstanceOf[Int] + 5)
vy = (if (java.lang.Math.random() < 0.5) -1 else 1) * 10
}

override def paint(graphics: Graphics) {
val g = graphics.asInstanceOf[Graphics2D]
g.setPaint(new Color(0, 0, 0))
g.fillRect(0, 0, 600, 400)
g.setPaint(new GradientPaint(0, 0, new Color(40, 40, 40), 0, 20, new Color(80, 80, 80)))
g.fillRect(0, 0, 600, 20)
g.setPaint(new GradientPaint(0, 380, new Color(80, 80, 80), 0, 400, new Color(40, 40, 40)))
g.fillRect(0, 380, 600, 20)
bar(g, 2, y1, 22)
bar(g, 598, y2, 578)
g.setPaint(new Color(255, 255, 255))
g.fillOval(xb - 5, yb - 5, 10, 10)
}

def run() {
initBall()
setPreferredSize(new Dimension(600, 400))
setFocusable(true)
addKeyListener(new KeyAdapter() {
override def keyPressed(e: KeyEvent) {
val code = e.getKeyCode()
if (code == KeyEvent.VK_UP) {
v2 = -5
} else if (code == KeyEvent.VK_DOWN) {
v2 = 5
} else if (code == KeyEvent.VK_W) {
v1 = -5
} else if (code == KeyEvent.VK_S) {
v1 = 5
}
}
override def keyReleased(e: KeyEvent) {
val code = e getKeyCode()
if (code == KeyEvent.VK_UP) {
if (v2 == -5) v2 = 0
} else if (code == KeyEvent.VK_DOWN) {
if (v2 == 5) v2 = 0
} else if (code == KeyEvent.VK_W) {
if (v1 == -5) v1 = 0
} else if (code == KeyEvent.VK_S) {
if (v1 == 5) v1 = 0
}
}
})
val frame = new JFrame("Scala Pong 4K - Use W/S and Up/Down")
frame.setLayout(new BorderLayout())
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
frame.setResizable(false)
frame.add(this, BorderLayout.CENTER)
frame.pack()
frame.setVisible(true)
new Timer(50, this).start()
}

}


And I just used ProGuard from the GUI (remember I was just hacking this), but somewhere along the way, I asked it to spit out the configuration:

-injars bin
-injars lib/scala-library.jar
-outjars output

-libraryjars /System/Library/Frameworks/JavaVM.framework/Versions/1.5.0/Classes/classes.jar

-optimizationpasses 3
-allowaccessmodification


# Keep - Applications. Keep all application classes, along with their 'main'
# methods.
-keepclasseswithmembers public class sp4k.* {
public static void main(java.lang.String[]);
}

# Keep names - Native method names. Keep all native class/method names.
-keepclasseswithmembers,allowshrinking class * {
native ;
}

# Remove - System method calls. Remove all invocations of System
# methods without side effects whose return values are not used.
-assumenosideeffects public class java.lang.System {
public static long currentTimeMillis();


And it actually keeps on going with lots of method lists of that "assumenosideeffects" stuff. Hmm. So I'll stop listing it here.

Thursday, January 17, 2008

Extension Methods vs. Traits & Implicit Conversions (a Scala question)

I personally prefer the idea of extension methods (most obviously in C#) vs. Scala's use of both traits/mixins and implicit conversions to achieve the same result. Simplifying the argument for each side, I find this:
  • Extension methods: Simpler, less bloat, more predictable, extensible by third parties.
  • Mixins: More flexible (since classes can override). Designer control for uniformity, but less extensible. Bloats compiled code. More complicated than static extension methods.
  • Implicit conversions: Extensible but results in more complicated semantics. (I understand that C# also has implicit conversion, but it's not meant for this use case to my understanding.)
So, Scala folks, why should I prefer mixins/implicits (the Scala way), and how do you decide how much to mix in and how much to extend by implicits?

My own idea is something I call a "role" which is a combination of a runtime-erased type alias with optional extension methods. But I won't go in depth right now.

Wednesday, January 16, 2008

Scala Pong 4K

Well, I've decided to enter the Java 4K Game contest again, but I didn't use Java. Launch my simple 2-player pong game from here:
Scala Pong 4K
Player 1 uses W and S. Player 2 uses Up and Down. Not too exciting. It doesn't even keep score. But it's under 4K.

I've watched Scala off and on for years. Most recently, I've been toying with making a Scala-look-alike of my own, but far closer to Java. My two biggest problems with Scala have been these:
  1. Scala is very different from Java, even though you can code in Java style. I'm sometimes willing to learn new, but I've been afraid others wouldn't, and I wanted something with popular support. Also, it would be nice to be able to write Java libraries in "JavaNG".
  2. It has a 2.5 (plus or minus) MB runtime library. Some people couldn't care less, but I love small. To me, 2.5 MB isn't anywhere close to small. Oh, and then the compiler is 4 MB or so, if you want that.
Well, Scala can be made usable from Java (just as people sometimes write C libraries in C++ or other languages), people are picking it up despite my fears, and I made a 4K game in it. So, maybe it's time to start learning it better.

Side notes, don't say "object Game extends JPanel" since the singleton objects create classes that make a static method for every method in the class or super class. Or at least all visible methods.

Also, even private vars create accessor methods. Closure vars don't, but they bring in the class IntRef (for ints). And simple closures like "(a) => a + 1" create a class with a half dozen methods defined in them. Maybe pack200 and gzip would take care of that, but pack200/gzip doesn't count for the Java 4K Game contest. In real life, it could be useful, though.

Oh, and I definitely used ProGuard.