blob: 5095495b327af420fcea13ff6998f6ae3cdc424a [file] [log] [blame]
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- ====================================================================== -->
<!-- Minesweeper in SVG -->
<!-- -->
<!-- @author thomas.deweese@kodak.com -->
<!-- @version $Id$ -->
<!-- ====================================================================== -->
<svg width="450" height="500" viewBox="0 0 450 500"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<script type="text/ecmascript">
<![CDATA[
//
// Global declarations
//
var mines0 = 8;
var mines1 = 40;
var mines2 = 99;
var boardSize = 1;
var numAcross = 16;
var numDown = 16;
var numMines = 40;
var correctFlags = 0;
var incorrectFlags = 0;
var minesLeft;
var board = document.getElementById("board")
var cover = document.getElementById("cover")
var boardArray = new Array(numAcross*numDown);
var shownArray = new Array(numAcross*numDown);
var flagArray = new Array(numAcross*numDown);
var isGameOver = true;
var xlinkNS = "http://www.w3.org/1999/xlink";
var svgNS = "http://www.w3.org/2000/svg";
function initBoard(evt) {
isGameOver = false;
minesLeft = numMines;
correctFlags = 0;
updateText("mines", "Mines Left: "+minesLeft);
updateText("boardsz", "Board Size: "+numAcross+"x"+numDown);
updateText("title", "");
updateText("working", "Working");
for(var y=0; y<numDown; y++) {
for(var x=0; x<numAcross; x++) {
boardArray[x+y*numAcross] = 0;
shownArray[x+y*numAcross] = false;
flagArray [x+y*numAcross] = false;
}
}
while (cover.lastChild != null) {
cover.removeChild(cover.lastChild);
}
while (board.lastChild != null) {
board.removeChild(board.lastChild);
}
for (var i=0; i<numMines; i++) {
var index = Math.round(Math.random()*(numDown*numAcross));
if ((index >= (numDown*numAcross)) ||
(boardArray[index]!=0)) {
i--;
} else {
boardArray[index] = -1;
}
}
for(var y=0; y<numDown; y++) {
var coverRow = document.createElementNS(svgNS, "g");
var boardRow = document.createElementNS(svgNS, "g");
var sum=0;
for (y1=y-1; y1<=y+1; y1++) {
if ((y1 >= 0) &&
(y1 < numDown) &&
(boardArray[y1*numAcross] == -1))
sum++;
}
for(var x=0; x<numAcross; x++) {
if (x-2 >= 0) {
for (y1=y-1; y1<=y+1; y1++) {
if ((y1 >= 0) &&
(y1 < numDown) &&
(boardArray[x-2+y1*numAcross] == -1))
sum--;
}
}
if (x+1 < numAcross) {
for (y1=y-1; y1<=y+1; y1++) {
if ((y1 >= 0) &&
(y1 < numDown) &&
(boardArray[x+1+y1*numAcross] == -1))
sum++;
}
}
if (boardArray[x+y*numAcross] == 0)
boardArray[x+y*numAcross] = sum;
var square = document.createElementNS(svgNS, "use");
square.setAttributeNS(null, "x", ""+(x*10));
square.setAttributeNS(null, "y", ""+(y*10));
square.setAttributeNS(null, "onclick",
"handleClick(evt, "+x+","+y+")");
var cov = square.cloneNode(true);
cov.setAttributeNS(null, "id", "cover"+x+"-"+y);
cov.setAttributeNS(xlinkNS, "href", "#blank");
var g = document.createElementNS(svgNS, "g");
g.appendChild(cov);
coverRow.appendChild(g);
square.setAttributeNS(null, "id", "square"+x+"-"+y);
if (boardArray[x+y*numAcross] == -1)
square.setAttributeNS(xlinkNS, "href", "#bomb");
else
square.setAttributeNS(xlinkNS, "href", "#square"+sum);
var g = document.createElementNS(svgNS, "g");
g.appendChild(square);
boardRow.appendChild(g);
}
cover.appendChild(coverRow);
board.appendChild(boardRow);
}
updateText("working", "");
updateText("title", "SVG Mines");
}
function handleClick(evt, x, y) {
if (isGameOver) return;
if ((evt.button == 1) ||
(evt.shiftKey)) {
placeFlag(x, y);
}
else if ((evt.button == 2) ||
(evt.ctrlKey)) {
showConnected(x, y);
}
else if (evt.button == 0) {
showSpot(x, y)
}
}
function placeFlag(x ,y) {
var idx = x + y*numAcross;
if (shownArray[idx]) return;
var elt = document.getElementById("cover"+x+"-"+y);
var nelt = elt.cloneNode(true);
if (flagArray[idx]) {
nelt.setAttributeNS(xlinkNS, "href", "#blank");
flagArray[idx] = false;
if (boardArray[idx] == -1) correctFlags--;
else incorrectFlags--;
minesLeft++;
} else {
nelt.setAttributeNS(xlinkNS, "href", "#flag");
flagArray[idx] = true;
if (boardArray[idx] == -1) correctFlags++;
else incorrectFlags++;
minesLeft--;
}
elt.parentNode.replaceChild(nelt, elt);
updateText("mines", "Mines Left: "+minesLeft);
if ((correctFlags == numMines) && (incorrectFlags == 0)) {
youWin();
}
}
function showSpot(x, y) {
var idx = x + y*numAcross;
if (shownArray[idx]) return;
var elt = document.getElementById("cover"+x+"-"+y);
elt.parentNode.parentNode.removeChild(elt.parentNode);
shownArray[idx] = true;
var val = boardArray[idx];
if (val == -1) {
gameOver();
} else if (val == 0) {
showConnected(x, y);
}
}
function showConnected(x, y) {
for (var dy=-1; dy<=1; dy++) {
if (y+dy < 0) continue;
if (y+dy >= numDown ) continue;
for (var dx=-1; dx<=1; dx++) {
if (x+dx < 0) continue;
if (x+dx >= numDown ) continue;
var idx = x+dx + (y+dy)*numAcross;
if (shownArray[idx]) continue;
if (flagArray[idx]) continue;
var elt = document.getElementById("cover"+(x+dx)+"-"+(y+dy));
elt.parentNode.parentNode.removeChild(elt.parentNode);
shownArray[idx] = true;
var val = boardArray[idx];
if (val == -1) {
gameOver();
} else if (val == 0) {
showConnected(x+dx, y+dy);
}
}
}
}
function gameOver() {
isGameOver=true;
updateText("title", "Game Over");
showBoard();
}
function showBoard() {
while (cover.lastChild != null) {
cover.removeChild(cover.lastChild);
}
var nc = numAcross*numDown;
for (var i=0; i<nc; i++)
shownArray[i] = true;
}
function youWin() {
isGameOver=true;
updateText("title", "You Win");
}
function updateText(id, lbl) {
var elt = document.getElementById(id);
var grp = elt.parentNode;
var newelt = elt.cloneNode(true);
if (newelt.firstChild == null) {
newelt.appendChild(document.createTextNode(lbl));
} else {
newelt.replaceChild(document.createTextNode(lbl), newelt.firstChild);
}
grp.replaceChild(newelt, elt);
}
function largerBoard(evt) {
if (boardSize == 1) return;
boardSize++;
if (boardSize == 1) {
numAcross = 16;
numDown = 16;
numMines = mines1;
}
else if (boardSize == 2) {
numAcross = 30;
numDown = 16;
numMines = mines2;
}
updateText("boardsz", "Board Size: "+numAcross+"x"+numDown);
updateText("title", "");
updateText("working", "Working");
setTimeout("initBoard()", 50);
}
function smallerBoard(evt) {
if (boardSize == 0) return;
boardSize--;
if (boardSize == 0) {
numAcross = 8;
numDown = 8;
numMines = mines0;
}
else if (boardSize == 1) {
numAcross = 16;
numDown = 16;
numMines = mines1;
}
updateText("boardsz", "Board Size: "+numAcross+"x"+numDown);
updateText("title", "");
updateText("working", "Working");
setTimeout("initBoard()", 50);
}
function newGame(evt) {
updateText("title", "");
updateText("working", "Working");
setTimeout("initBoard()", 50);
}
]]>
</script>
<defs>
<radialGradient id="bombGrad" gradientUnits="userSpaceOnUse"
cx="5" cy="6.5" fx="4" fy="5" r="2.5">
<stop offset="0%" stop-color="#FFF"/>
<stop offset="20%" stop-color="#888"/>
<stop offset="100%" stop-color="#000"/>
</radialGradient>
<path id="empty" style="fill:#AAA; stroke:#DDD; stroke-width:0.5;"
d="M0.25,0.25 h9.5 v9.5 h-9.5 v-9.5"/>
<g id="blank"
><rect style="fill:#AAA" width="10" height="10"
/><path style="fill:#DDD"
d="M0,0 h10 l-1,1 h-8 v8 l-1,1 v-10"
/><path style="fill:#888"
d="M10,10 h-10 l1,-1 h8 v-8 l1,-1 v10"
/></g>
<use id="square0" xlink:href="#empty"/>
<g id="square1" style="text-anchor:middle; font-size:8;"
><use xlink:href="#empty"
/><text x="5" y="8" fill="#008" pointer-events="none">1</text></g>
<g id="square2" style="text-anchor:middle; font-size:8;"
><use xlink:href="#empty"
/><text x="5" y="8" fill="#00C" pointer-events="none">2</text></g>
<g id="square3" style="text-anchor:middle; font-size:8;"
><use xlink:href="#empty"
/><text x="5" y="8" fill="#40F" pointer-events="none">3</text></g>
<g id="square4" style="text-anchor:middle; font-size:8;"
><use xlink:href="#empty"
/><text x="5" y="8" fill="#80C" pointer-events="none">4</text></g>
<g id="square5" style="text-anchor:middle; font-size:8;"
><use xlink:href="#empty"
/><text x="5" y="8" fill="#C0C" pointer-events="none">5</text></g>
<g id="square6" style="text-anchor:middle; font-size:8;"
><use xlink:href="#empty"
/><text x="5" y="8" fill="#F08" pointer-events="none">6</text></g>
<g id="square7" style="text-anchor:middle; font-size:8;"
><use xlink:href="#empty"
/><text x="5" y="8" fill="#F04" pointer-events="none">7</text></g>
<g id="square8" style="text-anchor:middle; font-size:8;"
><use xlink:href="#empty"
/><text x="5" y="8" fill="#F00" pointer-events="none">8</text></g>
<g id="bomb"
><use xlink:href="#empty"
/><rect fill="#000" x="4" y="3" width="2" height="2"
/><circle fill="url(#bombGrad)" cx="5" cy="6.5" r="2.5"
/><path fill="none" stroke="#000" stroke-width="0.3"
d="M5,3 c0,-1.5 1.5,-1.5 1.5,0 c0,1.5 1.5,1.5 1.5,0"
/></g>
<g id="flag"
><use xlink:href="#blank"
/><path d="M3.5,2 h.5 v6.5 h-1 l.5,-6.5"
/><path fill="crimson" d="M4,2 l4,1.5 l-4,1.5 z"
/></g>
</defs>
<g id="body">
<text id="title" x="25" y="60" font-size="40"
pointer-events="none">SVG Mines</text>
<text id="mines" x="250" y="30" font-size="20"
pointer-events="none">Mines Left: </text>
<text id="boardsz" x="250" y="54" font-size="20"
pointer-events="none">Board Size: </text>
<g transform="translate(242, 38)" fill="black">
<path onclick="largerBoard(evt)"
d="M2.5,0 l-2.5,7 h5 z"/>
<path onclick="smallerBoard(evt)"
d="M0,9 h5 l-2.5,7 z"/>
</g>
<g text-anchor="middle" >
<g id="help" >
<desc xml:space="preserve"
>Left: show
Right or Shift: place flag
Middle or Control: show surround</desc>
<circle cx="430" cy="18" r="12"
fill="grey" stroke="lightgrey" stroke-width="2" />
<text font-size="15" fill="blue" x="430" y="25"
pointer-events="none">?</text>
</g>
<g onclick="newGame()">
<desc xml:space="preserve">Start new game</desc>
<circle cx="430" cy="48" r="12"
fill="grey" stroke="lightgrey" stroke-width="2" />
<text font-size="10" fill="blue" x="430" y="53"
pointer-events="none">NG</text>
</g>
</g>
<g id="field" transform="translate(25,75) scale(2)"
onload="initBoard(evt)">
<g id="board">
</g>
<g id="cover">
</g>
</g>
<text id="working" fill="green" x="25" y="60" font-size="40"
pointer-events="none">Working</text>
</g>
<!-- ============================================================= -->
<!-- Batik sample mark -->
<!-- ============================================================= -->
<use xlink:href="batikLogo.svg#Batik_Tag_Box" />
</svg>