Cobra is a work-in-progress language that primarily targets the CLR. As far as I could tell, it doesn’t do much pioneering of new ideas, but it does attempt to bring together a bunch of basically good ideas in one place. These include:
- Mixed static and dynamic typing
- First class unit tests
- First class contracts
- Semantic Indenting
- Clean, expressive syntax
- Full-featured OOP (with interfaces, mixins, generics)
- Accurate decimal arithmetic
- High performance
How well does it do all this? Reasonably well, I have to say. The biggest missing features are closures, an an interactive runtime, but according to this presentation they’re on the way. I was excited about the mix of static and dynamic typing. I’ve wanted for a while to be able to hack something up without types and then go back through and tighten things up later. Cobra doesn’t seem like it’s 100% there. You can certainly omit type declarations, but it wasn’t always the case that a semantically valid program would run correctly without its type declarations. For example, there was one place where I left a member variable untyped, and then assigned a list to it in the initializer. It crashed like that. Adding the type declaration fixed it without any other changes.
The documentation is still quite spotty, as you might expect from an unfinished project. Most (all?) of the standard data types seem to be based directly on the .NET standard library, so you can go look some things up on MSDN.
Built-in unit tests are very cool. I could see them being overused and cluttering things up (it would be nice to have the option to pull them back out into another file when you’re done) but having real support from the runtime means that the compiler can tell you things about your test that other languages can’t. For example, assert 2 == 1 will give you an error message like:
(MonoType) info = nil this = Cell (MonoType) (2 == 1) = false
No need for awkward constructions like assert_equal or 2.should == 1
There is a JVM port underway, and also what looks like the beginnings of a port to the Objective-C runtime. Looks like one to watch.
Here’s the code (for version 0.8.0 and mono):
class TicTacToe
"""
Tic Tac Toe in Cobra
"""
def main is shared
players = Ring("X", "O")
board = Board(3)
player = players.next
cellsRemaining = 9
while true
print board
Console.write('Select a square, [player]: ')
Console.out.flush
try
answer = Console.readLine
Console.out.flush
catch System.ArgumentNullException
break
cell = board.getCell(answer)
if not cell
continue
cellsRemaining -= 1
cell.value = player
if cell.matchAny(3)
print board
print "[player] Wins!"
break
if cellsRemaining <= 0
print board
print "It's a Draw!"
break
player = players.next
class Ring
"""
A Ring class that takes its inputs and will return one
after the other ad infinitum.
"""
var _enumerator as IEnumerator
test
ring = Ring(1, 2, 3)
assert ring.next == 1
assert ring.next == 2
assert ring.next == 3
assert ring.next == 1
cue init(items as vari T)
base.init
_enumerator = List(items).getEnumerator
def next as T
if _enumerator.moveNext
return _enumerator.current
else
_enumerator.reset
return .next
class Cell
"""
A mesh-aware cell on the tic-tac-toe board. It keeps track
of its neighbors and can detect winners that way.
"""
pro address from var as String
pro value from var as String?
enum Dir
East
Southeast
South
Southwest
West
Northwest
North
Northeast
# Neighbors
var _neighbors = Dictionary()
cue init(address as String)
base.init
.address = address
for i in 8
_neighbors[i to Dir] = nil
cue init(address as String, value as String)
.init(address)
.value = value
def opposite(dir as Dir) as Dir
test
cell = Cell('2')
assert cell.opposite(Dir.East) == Dir.West
assert cell.opposite(Dir.West) == Dir.East
assert cell.opposite(Dir.Northeast) == Dir.Southwest
body
val = dir to int
val = (val + 4) % 8
return val to Dir
def add(dir as Dir, cell as Cell?)
if not _neighbors[dir]
_neighbors[dir] = cell
if cell, cell.add(.opposite(dir), this)
def addOtherSide(cell as Cell)
.add(Dir.East, cell)
cell.add(Dir.Northwest, _neighbors[Dir.North])
cell.addBelow(_neighbors[Dir.Northeast])
def addAbove(cell as Cell)
.add(Dir.South, cell)
cell.add(Dir.Northeast, _neighbors[Dir.East])
def addBelow(cell as Cell?) as Cell
if cell, cell.addAbove(this)
return this
def addBeside(cell as Cell?) as Cell
if cell, cell.addOtherSide(this)
return this
def match(direction as Dir, cell as Cell) as int
if .value cell.value
return 0
else if _neighbors[direction]
return 1 + _neighbors[direction].match(direction, this)
else
return 1
def match(dir as Dir, target as int) as bool
test
cell1 = Cell('1', "X")
cell2 = Cell('2', "X").addBeside(cell1)
cell3 = Cell('3', "X").addBeside(cell2)
cell4 = Cell('4', "X").addBelow(cell1)
cell5 = Cell('5', "X").addBeside(cell4)
cell6 = Cell('6', "X").addBeside(cell5)
cell7 = Cell('7', "X").addBelow(cell4)
cell8 = Cell('8', "X").addBeside(cell7)
cell9 = Cell('9', "X").addBeside(cell8)
assert cell1.match(Dir.Southeast, 3)
assert cell7.match(Dir.Southwest, 3)
assert cell7.match(Dir.Northeast, 3)
assert cell3.match(Dir.North, 3)
assert not cell6.match(Dir.Northeast, 3)
assert cell9.match(Dir.West, 3)
body
opp = .opposite(dir)
num = 1
if _neighbors[dir], num += _neighbors[dir].match(dir, this)
if _neighbors[opp], num += _neighbors[opp].match(opp, this)
return target == num
def matchAny(target as int) as bool
return .match(Dir.East, target) or _
.match(Dir.North, target) or _
.match(Dir.Northeast, target) or _
.match(Dir.Northwest, target)
def toString as String is override
if .value
return ' [.value] '
else
return '([.address])'
def rowToString(sb as StringBuilder, sep as String)
sb.append(this)
if _neighbors[Dir.East]
sb.append(sep)
_neighbors[Dir.East].rowToString(sb, sep)
def allToString(sb as StringBuilder, colsep as String, rowsep as String)
.rowToString(sb, colsep)
if _neighbors[Dir.South]
sb.append(Environment.newLine)
sb.append(rowsep)
sb.append(Environment.newLine)
_neighbors[Dir.South].allToString(sb, colsep, rowsep)
class Board
pro dim from var as int
pro head from var as Cell?
var _cells as IList
var _rowsep as String
cue init(dim as int)
ensure
dim >= 3
body
base.init
_dim = dim
_cells = List()
_constructBoard
_rowsep = "---"
for i in 1 : dim
_rowsep += "+---"
def _constructBoard
head = nil
tail = nil
for i in .dim
for j in .dim
cell = Cell((i*.dim + j + 1).toString)
_cells.add(cell)
if j == 0
head = tail = cell.addBelow(head)
else
tail = cell.addBeside(tail)
.head = _cells[0]
def getCell(address as String?) as Cell?
if not address, return nil
for cell in _cells
if cell.address == address
_cells.remove(cell)
return cell
return nil
def toString as String is override
sb = StringBuilder()
sb.append(Environment.newLine)
.head.allToString(sb, "|",_rowsep)
sb.append(Environment.newLine)
return sb.toString
0 Responses to “Tic-Tac-Toe: Cobra”