2007.08_Robocode–walki robotów_[Programowanie].pdf
(
1079 KB
)
Pobierz
439031305 UNPDF
programowanie
Walki robotów
– walki robotów
Marek Sawerwain
Jeśli ktoś zapyta, czym jest Robocode, to odpowiedź jest bardzo prosta. Robocode to zintegrowane
środowisko, którego zadaniem jest przeprowadzanie symulacji walk wirtualnych robotów bądź też botów,
a dokładniej czołgów. Wolno potraktować Robocode jako grę komputerową, choć bardzo specyiczną.
Bowiem naszym zadaniem jest stworzenie programu, który będzie sterował wirtualnym czołgiem
i walczył z wirtualnymi przeciwnikami sterowanymi przez inne programy.
cyjne, ponieważ poprzez zabawę uczy
programować. Język używany do pro-
gramowania robotów to Java. I jest to
pełna wersja tego języka – bez żadnych uproszczeń. Ma-
my dostępne API, za pomocą którego możemy sterować
naszym robotem. Występują zdarzenia, jak choćby traie-
nie naszego pojazdu, na które możemy reagować. Wydaje
się, że prosto jest napisać program do sterowania robotem.
To po części prawda – korzystanie z klas, metod i zdarzeń
sterujących robotem jest dość łatwe, ale opracowanie stra-
tegii na tyle dobrej, aby nasz czołg wygrywał pojedynki, to
już inny problem.
Jednak zaprojektowanie nawet najprostszych robo-
tów daje wielką frajdę, toteż w tym artykule postaram się
przedstawić wszystkie niezbędne wiadomości, jakie przy-
dadzą się na początku przygody z Robocode.
ostatnia nosi numer 1.3. Aplikacja została opracowana
w całości w Javie, co oznacza, że niestety musimy mieć za-
instalowaną Javę w systemie. Ramka pt.
Instalacja Javy i Ro-
bocode
podpowiada jak samodzielnie zainstalować Javę
i sam program Robocode. Niektóre dystrybucje, przykła-
dowo Debian, oferują gotowe pakiety, więc warto spraw-
dzić czy nie mamy gotowych pakietów np. z Javą.
Po uruchomieniu programu warto po obejrzeniu po-
czątkowej demonstracyjnej potyczki wybrać z menu opcję
File
, a następnie
New
, aby przeprowadzić testową bitwę. Po
wybraniu opcji
New
zobaczymy okno dialogowe podobne
do okna z Rysunku 1. Na początku interesująca jest tylko
zakładka
Robots
. Z listy dostępnych robotów za pomocą
kliknięcia przyciskiem myszy bądź przyciskiem o nazwie
Add
dodajemy wybrane roboty do listy
Selected Robots
.
Po wybraniu kilku robotów naciskamy przycisk
Start
Battle
i zobaczymy arenę, gdzie nasze czołgi będą to-
czyć bój na śmierć i życie, co widać również na Rysunku 3.
Warto także dopasować wielkość areny, jeśli korzystamy
z wysokiej rozdzielczości, to początkowa arena o rozmia-
rach 800x600 jest po prostu za mała i warto ją powiększyć,
co możemy zrobić w zakładce
BattleField
.
Wirtualna maszyna wojenna
Program Robocode to sprawdzony i dojrzały projekt roz-
wijany od roku 2001, czyli już niemal siedem lat. Jednak
mimo to nadal pojawiają się nowe wersje tego programu,
62
sierpień 2007
Robocode
W
ielką zaletą Robocode są walory eduka-
programowanie
Walki robotów
Listing 1.
Najmniej skomplikowany czołg – sam
się nie rusza, ale czasem strzela
Rysunek 1.
Początek walki, dodawanie robotów, które będą walczyć na arenie
package myrobots;
import robocode.*;
import java.awt.Color;
public
class
MyRobot_V1
extends
Robot
{
public
void
run
()
{
setColors
(
Color
.
blue
,
Color
.
re
d
,
Color
.
yellow
)
;
setScanColor
(
Color
.
WHITE
)
;
while
(
true
)
{
doNothing
()
;
}
}
public
void
onScannedRobot
(
Scan
nedRobotEvent
e
)
{
ire
(
1
)
;
}
public
void
onHitByBullet
(
HitBy
BulletEvent
e
)
{
turnRight
(
e
.
getBearing
())
;
ire
(
1
)
;
}
}
Rysunek 2 pokazuje, w jaki sposób zbu-
dowany jest nasz czołg. A składa się on z kil-
ku głównych części: podwozia, wieżyczki, na
której znajduje się działo oraz radaru. Natu-
ralnie za poruszanie się pojazdu odpowiada
podwozie, natomiast sama wieżyczka z dzia-
łem, identycznie jak w prawdziwym czołgu,
jest niezależna i możemy ją obracać w dowol-
nym kierunku. Podobnie radar – jest on rów-
nież niezależny i możemy nim obracać nieza-
leżnie od podwozia i działka. Ponieważ po-
ruszamy się po dwuwymiarowej arenie, to
zmiana nachylenia działa nie jest nam po-
trzebna, ale i tak opracowanie bardzo dobrej
strategii jest dużym wyzwaniem. Dość łatwo
steruje się czołgiem – np. przesunięcie się do
przodu o 100 pikseli realizujemy, pisząc po
prostu
ahead(100);
. Obrócenie radaru o 90
stopni w prawo sprowadza się do wywołania
metody
turnRadarRight(90);
. Natomiast
obrócenie samego pojazdu to zadanie dla me-
tod
turnRight
oraz
turnLeft
.
Pierwszy program
Listing 1. przedstawia najprostszy program
sterujący czołgiem na arenie. Możliwości na-
szego programu nie są naturalnie zbyt duże.
Nasz czołg zawsze stoi w miejscu, ale gdy
w jego polu rażenia znajdzie się inny czołg,
to oddajemy strzał w kierunku przeciwnika.
Reagujemy także na traienie. Jeśli ktoś strzeli
do naszego pojazdu i trai, to obrócimy czołg
w kierunku przeciwnika, a następnie odda-
my strzał w jego kierunku. Od razu trzeba
powiedzieć, iż taka bierna taktyka, powodu-
je, że w większości przypadków przegramy
pojedynki z innymi robotami.
Mimo tej uwagi warto zobaczyć jak jest
skonstruowany program, który realizuje tę
prostą strategię. Pierwsze trzy linie kodu
z Listingu 1. są typowe dla programów w Ja-
vie. Deklarujemy nowy pakiet, bowiem nasz
program sterujący to w rzeczywistości klasa.
Następnie włączamy dodatkowe klasy sło-
wem kluczowym
import
. Pierwszy z pakie-
tów, a dokładniej cały zestaw
robocode.*
, to
zbiór wszystkich przydatnych klas niezbęd-
nych do sterowania naszym robotem. Na-
stępnie włączamy deinicje kolorów, ponie-
waż nasz czołg możemy „pomalować” we-
dług własnego uznania.
W programie musi się znajdować przy-
najmniej jedna klasa, która dziedziczy z kla-
sy
Robot
. Klasa ta w naszym programie no-
si nazwę
MyRobot_V1
i jest konieczne, aby to
była klasa publiczna. Nie ma przeszkód, aby
tworzyć inne pomocnicze klasy do wykorzy-
stania w naszym programie sterującym. Cen-
tralnym elementem każdej klasy sterującej
Tabela 1.
Najważniejsze wydarzenia z życia robota-czołgu
Zdarzenie Krótki opis
onBulletHit(BulletHitEvent event); pocisk wystrzelony przez nasz czołg traił w przeciwnika
onHitByBullet(HitByBulletEvent event); przeciwnik traił w naszą maszynę
onDeath(DeathEvent event); zdarzenie pojawia się w momencie zniszczenia naszego robota
onWin(WinEvent event); zdarzenia wywoływane w momencie zwycięstwa rundy
onRobotDeath(RobotDeathEvent event); zdarzenie oznaczające zniszczenie innego robota
onScannedRobot(ScannedRobotE-
vent event);
onPaint(Graphics2D g);
używając tego zdarzenia, możemy zmienić graikę naszego
pojazdu
Rysunek 2.
Schemat budowy czołgu, którym
sterujemy
www.lpmagazine.org
63
pojawienie się tego zdarzenia oznacza, że radar „zauważył”
przeciwnika
onHitRobot(HitRobotEvent event); zdarzenie występuje w momencie, gdy zderzymy się z innym
robotem
onHitWall(HitWallEvent event); pojawienie się tego zdarzenia oznacza, iż nasz czołg zderzył
się ze ścianą okalającą arenę
programowanie
Walki robotów
robotem jest metoda publiczna
run()
. W tej
metodzie znajduje się pętla sterująca zacho-
waniem robota. W naszym przypadku w tre-
ści pętli
while
znajduje się tylko wywołanie
metody
doNothing
, która, zgodnie ze swo-
ją nazwą, nie wykonuje żadnych czynności.
Głównym zadaniem tej pętli jest sterowanie
naszym robotem, np. przesunięcie robota w kie-
runku przeciwnika lub wykonanie uniku.
Listing 1. zawiera także dwie inne meto-
dy. Pierwsza z nich o nazwie
onScannedRobot
jest wywoływana w momencie, gdy nasz ro-
bot wykryje innego robota. Argumentem tej
metody jest obiekt o typie
ScannedRobotE-
vent
. Otrzymany obiekt dostarcza kilku cen-
nych informacji jak np. odległość od przeciw-
nika. My jednak w naszym pierwszym robocie
tylko strzelamy w kierunku przeciwnika. Od-
danie strzału jest wykonywane w momencie
wywołania metody
ire
. Jej argumentem jest
siła ognia. Jednak trzeba pamiętać, że odda-
nie strzału powoduje nagrzewanie się działa,
jakim dysponuje nasz czołg. Dlatego nie może-
my ciągle strzelać z maksymalną siłą.
Druga metoda o nazwie
onHitByBullet
jest wywoływana w momencie, gdy nasz po-
jazd jest traiony przez przeciwnika, a jej treść
jest następująca:
turnRight(e.getBearing());
ire(1);
Metoda
turnRight
powoduje obrócenie na-
szej maszyny w kierunku, z którego otrzyma-
liśmy traienie. Informacja o tym, o ile stopni
należy się obrócić jest dostarczona w obiekcie
o typie
HitByBulletEvent
. Wartość kąta (w
przedziale od -180 do 180 stopni) jest zwra-
cana przez funkcję
getBearing
. Obracając się
w prawo (albo w lewo, jeśli kąt będzie miał
ujemną wartość), obrócimy się dokładnie w
kierunku, z którego otrzymaliśmy traienie.
Po wykonaniu obrotu oddamy strzał (meto-
da
ire
) w kierunku przeciwnika.
Instalacja Javy oraz Robocode
Niewielkie innowacje
Zamiast bezczynnie stać w miejscu, lepiej,
aby nasz pojazd znajdował się w ciągłym ru-
chu. Listing 2. pokazuje, w jaki sposób zre-
alizować poruszanie się naszego robota na
ścieżce przypominającej ósemkę. Nie zwa-
żając na wymiary areny i położenie naszego
robota, możemy za pomocą ciągu wywołań
turnLeft
,
ahead
i
turnRight
poruszać pojaz-
dem. Taka zmiana taktyki powoduje, że nasz
robot staje się trudniejszym przeciwnikiem.
Możemy zaproponować również taki sposób
poruszania się:
Jeśli system przez nas używany nie zawie-
ra Javy, to warto sprawdzić czy w dystry-
bucji której używamy, istnieją odpowiednie
pakiety. Interesuje nas Java w jak najnow-
szej wersji, np. wersja 1.5.09, może to być
zarówno pakiet uruchomieniowy JRE bądź
całe kompletne środowisko JDK. Jeśli na-
sza ulubiona dystrybucja nie zawiera od-
powiednich pakietów, to musimy zainstalo-
wać środowisko Javy, np. JDK, samodziel-
nie, co wbrew pozorom jest dość łatwe. Ze
strony
java.sun.com
ściągamy odpowied-
ni pakiet dla Linuksa. Dobrym wyborem jest
środowisko JDK wraz z programem Netbe-
ans. Odpowiedni plik może nosić następu-
jącą nazwę:
pu do Javy, jeśli w zmiennej PATH nie został
dopisany katalog z Javą):
/opt/jdk/bin/java -jar robocode.jar
Zostaniemy zapytani tylko o katalog docelo-
wy i po wyrażeniu zgody na jego utworzenie
po krótkiej chwili zostaną przegrane wszyst-
kie potrzebne pliki. Jeśli do zmiennej PATH
nie dopisaliśmy ścieżki do Javy, trzeba do-
datkowo poprawić skrypt startujący Robo-
code o nazwie
robocode.sh
. Zmieniamy
następującą linię:
java -Xmx512M -Dsun.io.useCanonCache
s=false -jar robocode.jar,
ahead(100);
turnGunRight(360);
back(100);
turnGunRight(360);
dopisując pełną ścieżkę do Javy:
jdk-1_5_0_09-nb-5_5-linux.bin
Jeśli mamy już ten plik, nadajemy mu atry-
but „wykonywalny”, np. z konsoli będzie to
przedstawiać się następująco:
/opt/jdk150_09/bin/java -Xmx512M
-Dsun.io.useCanonCaches=false -jar
robocode.jar.
Nasz pojazd jedzie sto pikseli do przodu, na-
stępnie obraca wieżyczkę o
360
stopni, cofa
się o
100
pikseli i ponownie obraca wieżycz-
kę. Taka zmiana również spowoduje, że nasz
czołg stanie się bardziej wymagającym prze-
ciwnikiem.
Jednak nie zawsze należy sterować ro-
botem w metodzie
run()
. Bardzo często le-
piej zmieniać położenie maszyny w zależno-
ści od wydarzeń, np. jeśli zostaliśmy traieni,
warto przesunąć naszego robota o kilka pik-
seli w prawo bądź w lewo, aby uniknąć na-
stępnego traienia. Jak to zrobić? Nie jest to
trudne, bo następujące trzy linie kodu reali-
zują to zadanie:
chmod +x jdk-1_5_0_09-nb-5_5-
linux.bin
Pozostaje jeszcze problem kompilatora
jikes
stosowanego przez Robocode do kompila-
cji programów sterujących. Jednakże nie
musimy się martwić czy posiadamy ten
kompilator w systemie, ponieważ Roboco-
de zawiera kod źródłowy kompilatora
jikes
i przy próbie kompilacji programu sterują-
cego następuje sprawdzenie czy posiada-
my wspomniany kompilator. Jeśli nie, to
Robocode samodzielnie dokona kompila-
cji, a to oznacza, że musimy mieć zainsta-
lowany kompilator C++, czyli pakiet GCC.
Jest on obecny we wszystkich dystrybu-
cjach, więc przy jego braku wystarczy tyl-
ko doinstalować potrzebny pakiet zwiera-
jący
gcc
, a dokładnie rzecz ujmując – pa-
kiet z programem
g++
, czyli kompilator dla
języka C++.
Po uruchomieniu programu instalacyjnego
– koniecznie w środowisku X – zobaczymy
graiczny instalator, gdzie klikając przyciski
Next
oraz wybierając nazwy katalogów, za-
instalujemy Javę oraz graiczne środowisko
pracy Netbeans.
Po instalacji Javy możemy przystąpić
do instalacji Robocode, choć warto spraw-
dzić czy nasza ulubiona dystrybucja nie ofe-
ruje takiego pakietu. Jeśli nie, to ściągamy
ze strony domowej wersję instalacyjną – jest
to plik o nazwie
robocode.jar
. Warto, by to
była jak najnowsza wersja stabilna. Sama in-
stalacja programu sprowadza się do urucho-
mienia procesu instalacji np. w następujący
sposób (musimy podać pełną ścieżkę dostę-
turnRight(relative_angle(90 -
(getHeading() - e.getHeading())));
ahead(dist);
dist *= -1;
Wywołanie metody
turnRight
powoduje ob-
rót o
90
stopni względem kierunku, z jakie-
go uzyskaliśmy traienie. A zadaniem funk-
64
sierpień 2007
programowanie
Walki robotów
Listing 2.
Modyikacja metody
Run
. Nasz czołg
będzie się poruszał po trasie przypominającej
ósemkę
public
void
run
()
{
setColors
(
Color
.
red
,
Color
.
blue
,
C
olor
.
green
)
;
turnLeft
(
45
)
;
while
(
true
)
{
ahead
(
100
)
;
turnRight
(
90
)
;
ahead
(
100
)
;
turnRight
(
90
)
;
ahead
(
100
)
;
turnRight
(
90
)
;
ahead
(
200
)
;
turnLeft
(
90
)
;
ahead
(
100
)
;
turnLeft
(
90
)
;
ahead
(
100
)
;
turnLeft
(
90
)
;
ahead
(
100
)
;
}
Rysunek 3.
Arena, na której toczy się walka
pełną siłą ognia, ponieważ możemy nie tra-
ić i stracimy niepotrzebnie sporo energii.
Dlatego będziemy strzelać standardową si-
łą ognia
ire(1)
, jeśli przeciwnik jest dale-
ko, powiedzmy ponad
200
pikseli, lub nasz
poziom energii jest mniejszy niż
15
jedno-
stek. Następnie – jeśli przeciwnik jest bliżej,
to strzelamy dwukrotną siłą ognia oraz osta-
tecznie – jeśli przeciwnik znajduje się bliżej
niż
50
pikseli, strzelamy potrójną siłą ognia.
Ostatecznie zapiszemy to w następujący
sposób:
cji
relative_angle
(Listing 3) jest konwersja
wartości kąta do wartości z przedziału
-180
do
180
stopni.
Inaczej mówiąc, zadaniem pierwszej linii
kodu jest wykonanie obrotu w prawo bądź
w lewo, pomimo nazwy metody
turnRight
,
ponieważ wartość kąta może być dodatnia al-
bo ujemna. Następnie przesuwamy czołg do
przodu albo w tył, co też zależy od wartości
dist
. Wartość ta na przemian jest dodatnia
i ujemna, co zapewnia trzecia linijka, gdzie
mnożymy
dist
przez
-1
. W ten sposób, jeśli
przeciwnik strzela do nas serią, mamy dużą
pewność, że zostaniemy traieni tylko raz na
początku, a pozostałe traienia nas ominą.
Inną modyikacją jest zmiana siły ognia
w zależności od tego, jak daleko znajduje
się przeciwnik. Jeśli zostanie on wykryty
przez radar, to następuje wywołanie meto-
dy
onScannedRobot
, a jej argumentem jest
obiekt o typie
ScannedRobotEvent
. Odczy-
tanie odległości do przeciwnika jest dość
proste:
enemyDist = e.getDistance( );
Każdy strzał zabiera określoną ilość energii
(i jeszcze nagrzewa działo, więc nie możemy
strzelać zawsze pełną siłą ognia), toteż jeśli
przeciwnik jest daleko, to nie warto strzelać
if ( enemyDist > 200 || getEnergy( )
< 15 ) {
ire ( 1 );
} else if ( enemyDist > 50 ) {
ire ( 2 );
} else {
ire ( 3 );
}
Listing 3.
Pomocnicza funkcja do korekcji warto-
ści kąta względnego
public
double
relative_
angle
(
double
angle
)
{
double
good_angle
=
angle
;
if
(
angle
>
-
180
&&
angle
<=
180
)
{
return
angle
;
}
while
(
good_angle
<=
-
180
)
{
good_angle
+=
360
;
}
while
(
good_angle
>
180
)
{
good_angle
-=
360
;
}
return
good_angle
;
}
Rysunek 4.
Edycja programu sterującego naszym wirtualnym wojownikiem
www.lpmagazine.org
65
programowanie
Walki robotów
W powyższym kodzie milcząco zakładamy
też, że radar oraz działo obracają się razem
z całą wieżyczką. Możemy to zmienić, wy-
korzystując metodę
setAdjustRadarForGun-
Turn
. Jeśli podamy wartość
true
albo
false
,
pozwalamy na niezależne sterowanie rada-
rem bądź blokujemy tę możliwość. Podobnie
jest w przypadku działa – sposób funkcjono-
wania ustala się za pomocą metody
setAdju-
stGunForRobotTurn
.
Łatwo też sprawdzić temperaturę działa
i, jeśli wynosi ona zero, możemy strzelać ca-
łą dostępną mocą, co realizujemy za pomocą
następującego kodu:
if (getGunHeat() == 0) {
ire(Rules.MAX_BULLET_POWER);
}
Śledzenie przeciwnika
Zwiększenie mobilności naszego robota to na-
turalnie krok w dobrą stronę, jednak oprócz
tego naszego robota należy też nauczyć zna-
cznie lepiej strzelać. Dobrą taktyką będzie śle-
dzenie przeciwnika i strzelanie w jego kie-
runku tak długo, aż zostanie zniszczony. Do-
datkowo z każdą turą będziemy się przybli-
żać do śledzonego przeciwnika, w ten sposób
polepszając skuteczność ognia.
Łatwo jest przedstawić taką taktykę, ale
znacznie trudniej napisać odpowiedni kod.
Listing 4. przedstawia wszystkie najważniej-
sze fragmenty implementacji przedstawio-
nej strategii. Implementacja, jak widać, zosta-
ła dokonana tylko w metodach
run
oraz
on-
ScannedRobot
.
Na początku musimy zadeklarować trzy
dodatkowe zmienne. Pierwsza z nich,
GunAn-
gleToEnemy
, to kąt, pod jakim należy obrócić
działo, aby strzelać do przeciwnika. Nasz pro-
gram sterujący w trakcie pracy będzie ciągle
uaktualniał tę wartość, aby zawsze celować
w śledzony czołg. W drugiej zmiennej
Ene-
myTrackingName
znajduje się nazwa śledzo-
nego robota. Ostatnia trzecia zmienna
turns
służy nam do swoistego odliczania, np. po
piętnastu turach do zmiennej
EnemyTracking
Name
wpisujemy
null
, co oznacza, że będzie-
my szukać innego przeciwnika. Wymienione
piętnaście tur wystarczy do zniszczenia na-
mierzonego robota.
W metodzie
run
nie wykonujemy zbyt
wielu czynności, jednak każda z nich jest bar-
dzo ważna. Po pierwsze – przed wejściem do
pętli
while
wpisujemy do
EnemyTrackingNa-
me
wartość
null
. Zezwalamy na niezależ-
ne sterowanie działem od podwozia, ustala-
my początkową wartość kąta oraz zerujemy
zmienną
turns
.
Natomiast w samej pętli
while
zawsze
za pomocą metody
turnGunRight
staramy
się mieć działo obrócone w kierunku prze-
ciwnika. Zmieniamy także kąt o
10
stopni,
co się przydaje w momencie, gdy aktualnie
nasz czołg nie ma namierzonego przeciwni-
ka. Następnie zwiększamy wartość zmiennej
turns
i jeśli przekroczyła ona wartość piętna-
stu, usuwamy nazwę śledzonego robota.
W metodzie
onScannedRobot
wykonuje-
my najważniejszą „robotę” związaną ze śle-
dzeniem. Na samym początku pierwsza in-
strukcja warunkowa sprawdza czy nazwa śle-
dzonego robota jest różna od
null
oraz czy ak-
tualnie przekazany obiekt typu
ScannedRobo-
Listing 4.
Śledzenie przeciwnika
private
double
GunAngleToEnemy
=
0
;
private
String
EnemyTrackingName
;
private
int
turns
;
public
void
run
()
{
EnemyTrackingName
=
null
;
setAdjustGunForRobotTurn
(
true
)
;
GunAngleToEnemy
=
15
;
turns
=
0
;
while
(
true
)
{
turnGunRight
(
GunAngleToEnemy
)
;
GunAngleToEnemy
+=
10
;
turns
++;
if
(
turns
>
15
)
{
EnemyTrackingName
=
null
;
}
}
}
public
void
onScannedRobot
(
ScannedRobotEvent
e
)
{
if
(
EnemyTrackingName
!=
null
&&
!
e
.
getName
()
.
equals
(
EnemyTrackingName
))
{
return
;
}
if
(
EnemyTrackingName
==
null
)
{
EnemyTrackingName
=
e
.
getName
()
;
turns
=
0
;
}
if
(
e
.
getDistance
()
>
150
)
{
GunAngleToEnemy
=
relative_angle
(
e
.
getBearing
()
+
(
getHeading
()
-
getRadarHeading
()))
;
turnGunRight
(
GunAngleToEnemy
)
;
turnRight
(
e
.
getBearing
())
;
ahead
(
e
.
getDistance
()
-
140
)
;
return
;
}
GunAngleToEnemy
=
relative_angle
(
e
.
getBearing
()
+
(
getHeading
()
-
getRadarHeading
()))
;
turnGunRight
(
GunAngleToEnemy
)
;
ire
(
3
)
;
if
(
e
.
getDistance
()
<
100
)
{
if
(
e
.
getBearing
()
>
-
90
&&
e
.
getBearing
()
<=
90
)
{
back
(
40
)
;
}
else
{
ahead
(
40
)
;
}
}
scan
()
;
}
66
sierpień 2007
Plik z chomika:
SOLARIX33
Inne pliki z tego folderu:
2006.01_Koder plików w formacie OGG_[Programowanie].pdf
(722 KB)
2007.06_Piękno fraktali_[Programowanie].pdf
(1778 KB)
2008.11_GanttProject_[Programowanie].pdf
(1014 KB)
2007.04_USB Device Explorer_[Programowanie].pdf
(1134 KB)
2006.09_QT, PyQT – szybkie tworzenie baz danych_[Programowanie].pdf
(1319 KB)
Inne foldery tego chomika:
Administracja
Aktualnosci
Audio
Bazy Danych
Bezpieczenstwo
Zgłoś jeśli
naruszono regulamin