singrdk/base/Applications/Pong/Pong.sg

511 lines
16 KiB
Plaintext
Raw Normal View History

2008-03-05 09:52:00 -05:00
////////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: Pong.cs
//
// Note:
//
using System;
using System.Text;
using System.GC;
using System.Runtime.Remoting;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Collections;
using System.Diagnostics;
using Microsoft.Singularity;
using Microsoft.Singularity.Directory;
using Microsoft.Singularity.V1.Services;
using Microsoft.Singularity.Io;
using Microsoft.Singularity.Channels;
using Keyboard = Microsoft.Singularity.Io.Keyboard;
namespace Microsoft.Singularity.Applications
{
public class Pong
{
const uint Black = 0x000000;
const uint Blue = 0x0000ff;
const uint Green = 0x00ff00;
const uint Red = 0xff0000;
const uint White = 0xffffff;
const int screenWidth = 1024;
const int screenHeight = 768;
const int paddleWidth = 16;
const int paddleHeight = 128;
const int ballSize = 8;
const double paddleSpeed = 0.20;
const double ballSpeed = 0.35;
const double ballStartAngle = Math.PI / 8;
const double ballMaxAngle = Math.PI / 3;
const int screenMinX = 0;
const int screenMinY = 0;
const int screenMaxX = screenMinX + screenWidth - 1;
const int screenMaxY = screenMinY + screenHeight - 1;
const int paddleMin = screenMinY;
const int paddleMax = screenMaxY - paddleHeight;
const int ballMinX = screenMinX + paddleWidth;
const int ballMaxX = screenMaxX - paddleWidth - ballSize;
const int ballMinY = screenMinY;
const int ballMaxY = screenMaxY - ballSize;
const int paddleInit = screenMinY + ((screenHeight - paddleHeight) / 2);
static double ballX = 0.0;
static double ballY = 0.0;
static double ballAngle = 0.0;
static int ballDrawX = 0;
static int ballDrawY = 0;
static double[] paddlesY = new double [2] { paddleInit, paddleInit };
static int[] paddlesDrawX = new int [2] { screenMinX,
screenMaxX - paddleWidth + 1 };
static int[] paddlesDrawY = new int [2] { paddleInit, paddleInit };
static bool[] inputUp = new bool [2] { false, false };
static bool[] inputDown = new bool [2] { false, false };
static int[] scores = new int [2] { 0, 0 };
static int lastWinner = 0;
static bool done = false;
static bool playing = false;
static Random random = new Random();
public static KeyboardDeviceContract.Imp:Ready OpenKeyboard(string! devName)
{
KeyboardDeviceContract.Exp! exp;
KeyboardDeviceContract.Imp! imp;
KeyboardDeviceContract.NewChannel(out imp, out exp);
// get NS endpoint
DirectoryServiceContract.Imp ns = DirectoryService.NewClientEndpoint();
bool success = false;
ErrorCode errorOut;
bool ok = SdsUtils.Bind(devName, ns, exp, out errorOut);
if (!ok) {
if (errorOut == ErrorCode.ChannelClosed)
{
throw new Exception("Encountered a ChannelClosed while opening Keyboard");
}
}
else {
success = true;
}
/*
// ns.SendBind(Bitter.FromString2(devName),exp);
switch receive {
case ns.AckBind():
success = true;
break;
case ns.NakBind(rejected,error):
delete rejected;
break;
case ns.ChannelClosed():
delete ns;
throw new Exception("ns unexpectedly closed");
}
*/
if (!success) {
DebugStub.WriteLine("OpenKeyboard lookup of {0} failed\n",
__arglist(devName));
delete imp;
delete ns;
return null;
}
switch receive {
case imp.Success():
break;
case imp.ContractNotSupported():
throw new Exception("Contract not supported");
case imp.ChannelClosed():
throw new Exception("Didn't imp.RecvActConnect");
}
delete ns;
return imp;
}
public static VideoDeviceContract.Imp:Ready OpenVideo(string! devName)
{
VideoDeviceContract.Exp! exp;
VideoDeviceContract.Imp! imp;
VideoDeviceContract.NewChannel(out imp, out exp);
// get NS endpoint
DirectoryServiceContract.Imp ns = DirectoryService.NewClientEndpoint();
bool success = false;
ns.SendBind(Bitter.FromString2(devName),exp);
switch receive {
case ns.AckBind():
success = true;
break;
case ns.NakBind(rejected, error):
delete rejected;
break;
case ns.ChannelClosed():
delete ns;
throw new Exception("ns unexpectedly closed");
}
if (!success) {
DebugStub.WriteLine("OpenVideo lookup of {0} failed\n",
__arglist(devName));
delete imp;
delete ns;
return null;
}
switch receive {
case imp.Success():
break;
case imp.ContractNotSupported():
throw new Exception("Contract not supported");
case imp.ChannelClosed():
throw new Exception("Didn't imp.RecvActConnect");
}
delete ns;
return imp;
}
private static void DrawPaddle(VideoDeviceContract.Imp:Ready! video, int paddle, bool erase)
{
video.SendFill(paddlesDrawX[paddle],
paddlesDrawY[paddle],
paddlesDrawX[paddle] + paddleWidth - 1,
paddlesDrawY[paddle] + paddleHeight - 1,
erase ? Black : Blue);
video.RecvAckFill();
}
private static void RedrawPaddle(VideoDeviceContract.Imp:Ready! video, int paddle)
{
if (paddlesDrawY[paddle] != (int) paddlesY[paddle]) {
DrawPaddle(video, paddle, true);
paddlesDrawY[paddle] = (int) paddlesY[paddle];
DrawPaddle(video, paddle, false);
}
}
private static void MovePaddle(VideoDeviceContract.Imp:Ready! video, int paddle)
{
paddlesY[paddle] += GetPaddleState(paddle) * paddleSpeed;
if (paddlesY[paddle] > paddleMax) {
paddlesY[paddle] = paddleMax;
}
else if (paddlesY[paddle] < paddleMin) {
paddlesY[paddle] = paddleMin;
}
RedrawPaddle(video, paddle);
}
private static bool PaddleHit(int paddle)
{
return ((ballY > paddlesY[paddle] - ballSize) &&
(ballY < paddlesY[paddle] + paddleHeight));
}
private static void ResetBall()
{
ballY = screenMinY + ((screenHeight - ballSize) / 2);
ballAngle = (random.NextDouble() * ballStartAngle) -
(ballStartAngle / 2);
if (lastWinner == 0) {
ballX = ballMinX;
if (ballAngle < 0) {
ballAngle += 2 * Math.PI;
}
}
else {
ballX = ballMaxX;
ballAngle += Math.PI;
}
ballDrawX = (int) ballX;
ballDrawY = (int) ballY;
}
private static void DrawBall(VideoDeviceContract.Imp:Ready! video, bool erase)
{
video.SendFill(ballDrawX, ballDrawY,
ballDrawX + ballSize - 1, ballDrawY + ballSize - 1,
erase ? Black : Red);
video.RecvAckFill();
}
private static void RedrawBall(VideoDeviceContract.Imp:Ready! video)
{
if (ballDrawX != (int) ballX || ballDrawY != (int) ballY) {
DrawBall(video, true);
ballDrawX = (int) ballX;
ballDrawY = (int) ballY;
DrawBall(video, false);
}
}
private static void ReflectBallY(int line)
{
ballY = (2 * line) - ballY;
ballAngle = (2 * Math.PI) - ballAngle;
}
private static void SetAngle(int paddle)
{
// Find out where the ball hit the paddle, from -1 to 1.
double ratio = ((ballY + (ballSize / 2)) -
(paddlesY[paddle] + (paddleHeight / 2))) /
((paddleHeight + ballSize) / 2);
// Set the new angle accordingly.
ballAngle = ratio * ballMaxAngle;
if (paddle == 0) {
if (ballAngle < 0) {
ballAngle += 2 * Math.PI;
}
}
else {
ballAngle = Math.PI - ballAngle;
}
}
private static void MoveBall(VideoDeviceContract.Imp:Ready! video)
{
ballY += Math.Sin(ballAngle) * ballSpeed;
if (ballY < ballMinY) {
ReflectBallY(ballMinY);
}
else if (ballY > ballMaxY) {
ReflectBallY(ballMaxY);
}
ballX += Math.Cos(ballAngle) * ballSpeed;
if (ballX < ballMinX) {
if (PaddleHit(0)) {
SetAngle(0);
}
else {
lastWinner = 1;
playing = false;
}
}
else if (ballX > ballMaxX) {
if (PaddleHit(1)) {
SetAngle(1);
}
else {
lastWinner = 0;
playing = false;
}
}
RedrawBall(video);
}
private static int GetPaddleState(int paddle)
{
if (inputUp[paddle]) {
return -1;
}
else if (inputDown[paddle]) {
return 1;
}
else {
return 0;
}
}
private static void SetStateFromChar(char c, bool @state)
{
switch (c) {
case (char)Keyboard.Keys.UP_ARROW:
inputUp[1] = @state;
break;
case (char)Keyboard.Keys.DOWN_ARROW:
inputDown[1] = @state;
break;
case 'a':
inputUp[0] = @state;
break;
case 'z':
inputDown[0] = @state;
break;
}
}
private static void FillScreen(VideoDeviceContract.Imp:Ready! video, uint color)
{
video.SendFill(screenMinX, screenMinY, screenMaxX, screenMaxY, color);
video.RecvAckFill();
}
private static void ClearScreen(VideoDeviceContract.Imp:Ready! video)
{
FillScreen(video, Black);
}
private static void FlashScreen(VideoDeviceContract.Imp:Ready! video)
{
FillScreen(video, Red);
Sleep(1000);
FillScreen(video, Green);
Sleep(1000);
FillScreen(video, Blue);
Sleep(1000);
}
private static void ShowMessage(VideoDeviceContract.Imp:Ready! video)
{
ClearScreen(video);
for (int i = 0; i < 100; i++) {
Console.WriteLine();
}
Console.Write(" ");
Console.Write("Left: {0,-10}", scores[0]);
Console.Write(" ");
Console.Write("Right: {0,-10}", scores[1]);
Console.WriteLine();
Console.WriteLine();
Console.Write(" ");
Console.Write("Press space to begin or escape to quit...");
Console.WriteLine();
DrawPaddle(video, 0, false);
DrawPaddle(video, 1, false);
DrawBall(video, false);
}
private static void Sleep(int multiplier)
{
for (int j = 0; j < multiplier; j++) {
for (int i = 0; i < 10000; i++) {
// Do nothing.
}
}
}
public static int Main(string[] args)
{
VideoDeviceContract.Imp video = OpenVideo("/dev/video");
Console.WriteLine("Singularity Pong (PID={0})",
ProcessService.GetCurrentProcessId());
if (video == null) {
Console.WriteLine("Can only play pong on a graphic display.");
return 1;
}
KeyboardDeviceContract.Imp keyboard = OpenKeyboard("/dev/keyboard");
if (keyboard == null) {
// TODO: show keyboardless slides with timer?
delete video;
return 1;
}
ShowMessage(video);
while (!done) {
uint key = 0;
keyboard.SendPollKey();
switch receive
{
case keyboard.AckKey(ikey):
key = ikey;
break;
case keyboard.NakKey():
key = 0;
break;
case keyboard.ChannelClosed():
throw new Exception("Didn't get keyboard closed.");
}
char c = (char) (key & (uint) Keyboard.Qualifiers.KEY_BASE_CODE);
if ((key & (uint) Keyboard.Qualifiers.KEY_DOWN) != 0) {
switch (c) {
case (char) Keyboard.Keys.ESCAPE:
done = true;
break;
case ' ':
if (!playing) {
ResetBall();
ClearScreen(video);
DrawPaddle(video, 0, false);
DrawPaddle(video, 1, false);
DrawBall(video, false);
playing = true;
}
break;
default:
SetStateFromChar(c, true);
break;
}
}
else if ((key & (uint) Keyboard.Qualifiers.KEY_UP) != 0) {
SetStateFromChar(c, false);
}
if (playing) {
MovePaddle(video, 0);
MovePaddle(video, 1);
MoveBall(video);
if (!playing) {
scores[lastWinner]++;
FlashScreen(video);
ShowMessage(video);
}
}
Sleep(10);
}
ClearScreen(video);
delete keyboard;
delete video;
return 0;
}
}
}