Code edit (1 edits merged)
Please save this source code
User prompt
Papers, Please: UML Edition
User prompt
1. Adım: Veri Mimarisi ve JSON Şeması Oyunun seviyelerini ve kağıtların içeriğini yönetecek beyin kısmını oluşturmak için bu promptu kullan: "Sistem analizi dersi için 'Papers, Please' tarzı 2 boyutlu bir belge inceleme oyunu geliştiriyorum. Bana oyunun veri yapısını tutacak kapsamlı bir JSON şeması ve bu veriyi okuyacak Sınıf (Class) yapılarını yaz. JSON şemasında şunlar olmalı: Seviye numarası, o seviyede kural kitabına eklenecek yeni kural metni, masaya gelecek belgelerin listesi. Her belge (paper) objesi içinde; belge ID'si, 'Müşteri Senaryosu' metni, 'UML Çizimi' görsel yolu (URL/Path), belgenin geçerli olup olmadığı (is_valid: boolean), eğer hatalıysa metindeki hangi kelimenin görseldeki hangi bölge (zone/hitbox) ile çeliştiğini tutan hata doğrulama verisi yer almalı." 2. Adım: Masa Arayüzü ve Sürükle-Bırak (Drag & Drop) Veri yapısı hazırlandıktan sonra, görsel tarafı çizdirmek için bu promptu kullan: "Şimdi bu oyun için temel oyun ekranı arayüzünü (UI) kodla. Ekranı bir çalışma masası gibi düşün. Ekranın sol altında tıklandığında açılıp kapanan bir 'Kural Kitabı' butonu/paneli olsun. Masanın ortasında, kullanıcı tarafından fare/parmak ile ekranın her yerine sürüklenebilen (Drag & Drop) iki adet kağıt objesi (Widget) yarat. Biri 'Metin Belgesi', diğeri 'Görsel Belge' olsun. Masanın sağ tarafına sabit duran iki adet büyük buton ekle: Yeşil 'ONAYLA' damgası ve Kırmızı 'REDDET' damgası." 3. Adım: İnceleme ve Bağlantı (Link/Inspection) Mekaniği Oyunun kalbi olan, çelişkileri bulma mantığını yazdırmak için bu promptu kullan: "Oyunun ana mekaniği olan 'Hata Tespiti' (Inspection) sistemini kurgulamak istiyorum. Müşteri senaryosu metnindeki kelimeleri tıklanabilir (seçilebilir) hale getir. UML görseli üzerinde de görünmez tıklanabilir alanlar (hitbox/zone) oluştur. Oyuncu metindeki bir kelimeye tıklayıp (seçili hale getirip), ardından görseldeki bir alana tıkladığında bu iki veriyi birbiriyle eşleştiren bir fonksiyon yaz. Eğer eşleştirilen bu iki alan, ilk adımda yarattığımız JSON verisindeki 'hata doğrulama' koordinatlarıyla uyuşuyorsa, ekranda 'TUTARSIZLIK BULUNDU' uyarısı çıksın ve oyuncuya belgeyi reddetme yetkisi verilsin." 4. Adım: Oyun Döngüsü, Damgalar ve Puanlama Kağıtları onaylayıp/reddedip diğer kağıda geçme işlemini yazdırmak için bu promptu kullan: "Masanın sağındaki Onayla (Yeşil) ve Reddet (Kırmızı) damgaları için oyun döngüsü (Game Loop) mantığını yaz. Kurallar şunlar: Eğer JSON verisinde belge doğruysa (is_valid: true) ve oyuncu ONAYLA basarsa +10 puan, REDDET basarsa -10 puan ve 'Ceza Fişi' uyarısı ver. Eğer belge hatalıysa (is_valid: false), oyuncu hatayı (bir önceki adımdaki fonksiyonla) bulmadan REDDET basamasın. Hatayı bulup REDDET basarsa +10 puan ver. Hatalı belgeye ONAYLA basarsa -10 puan ve ceza ver. Damga basıldıktan sonra mevcut kağıtlar ekrandan kaybolsun, kısa bir animasyon/gecikme ile sıradaki JSON belgesinin verileri masaya gelsin. Belgeler bitince 'Gün Bitti' ekranı çıksın." 5. Adım: İçerik ve Senaryo Üretimi (Data Doldurma) Kod altyapısı bittikten sonra, yapay zekayı bir "Bölüm Tasarımcısı" olarak kullanmak için bu promptu kullan: "Oyunun kod iskeleti ve arayüzü tamamlandı. Şimdi senden oyunun içeriğini (JSON datalarını) üretmeni istiyorum. Sistem Analizi ve Tasarımı (SAD) dersi 'UML Sınıf Diyagramları' konusu için ilk 3 seviyenin verisini yaz. Seviye: Temel Sınıf Yapısı (İsim, nitelik, metot bölmeleri). Seviye: Erişim Belirleyiciler (Public, Private, Protected). Seviye: Çokluluk (Multiplicity) ve Kalıtım (Inheritance). Bana toplam 15 farklı kağıt senaryosu (is_valid true ve false olanlar karışık) üret. Hatalı olanlar için yaratıcı UML çizim hataları ve bunlarla çelişen mantıklı müşteri metinleri kurgula."
User prompt
Please continue polishing my design document.
Initial prompt
Oyunun seviyelerini ve kağıtların içeriğini yönetecek beyin kısmını oluşturmak için bu promptu kullan: "Sistem analizi dersi için 'Papers, Please' tarzı 2 boyutlu bir belge inceleme oyunu geliştiriyorum. Bana oyunun veri yapısını tutacak kapsamlı bir JSON şeması ve bu veriyi okuyacak Sınıf (Class) yapılarını yaz. JSON şemasında şunlar olmalı: Seviye numarası, o seviyede kural kitabına eklenecek yeni kural metni, masaya gelecek belgelerin listesi. Her belge (paper) objesi içinde; belge ID'si, 'Müşteri Senaryosu' metni, 'UML Çizimi' görsel yolu (URL/Path), belgenin geçerli olup olmadığı (is_valid: boolean), eğer hatalıysa metindeki hangi kelimenin görseldeki hangi bölge (zone/hitbox) ile çeliştiğini tutan hata doğrulama verisi yer almalı."
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
level: 1,
currentDocIndex: 0
});
/****
* Classes
****/
var DiagramZone = Container.expand(function () {
var self = Container.call(this);
self.zoneText = new Text2('zone', {
size: 24,
fill: '#333333'
});
self.addChild(self.zoneText);
self.interactive = true;
self.linkedFrom = null;
self.zoneId = '';
self.correctWord = '';
self.setup = function (label, posX, posY, zoneId, correctWord) {
self.zoneText.setText(label);
self.x = posX;
self.y = posY;
self.zoneId = zoneId;
self.correctWord = correctWord;
};
self.highlight = function () {
tween(self, {
alpha: 0.6
}, {
duration: 150
});
};
self.unhighlight = function () {
tween(self, {
alpha: 1
}, {
duration: 150
});
};
return self;
});
var DocumentPair = Container.expand(function () {
var self = Container.call(this);
self.scenario = '';
self.umlDiagram = null;
self.errors = [];
self.selectableWords = [];
self.links = [];
self.isApproved = false;
self.isRejected = false;
self.setup = function (scenario, classData, errors) {
self.scenario = scenario;
self.errors = errors;
var textBox = LK.getAsset('textBox', {
anchorX: 0,
anchorY: 0
});
textBox.x = 50;
textBox.y = 50;
self.addChild(textBox);
var scenarioText = new Text2(scenario, {
size: 24,
fill: '#333333'
});
scenarioText.anchor.set(0, 0);
scenarioText.x = 70;
scenarioText.y = 70;
scenarioText.wordWrap = true;
scenarioText.wordWrapWidth = 850;
self.addChild(scenarioText);
var diagramBox = LK.getAsset('diagramBox', {
anchorX: 0,
anchorY: 0
});
diagramBox.x = 50;
diagramBox.y = 480;
self.addChild(diagramBox);
self.umlDiagram = new UMLClassDiagram();
self.umlDiagram.x = 250;
self.umlDiagram.y = 600;
self.umlDiagram.addClassBox(classData.name, classData.properties, classData.methods);
self.addChild(self.umlDiagram);
};
self.approve = function () {
self.isApproved = true;
LK.getSound('stamp').play();
var hasErrors = self.errors.length > 0 && !self.hasFoundAllErrors();
return !hasErrors;
};
self.reject = function () {
self.isRejected = true;
LK.getSound('stamp').play();
var hasErrors = self.errors.length > 0;
return hasErrors;
};
self.hasFoundAllErrors = function () {
return self.links.length >= self.errors.length;
};
return self;
});
var SelectableWord = Container.expand(function () {
var self = Container.call(this);
self.wordText = new Text2('word', {
size: 32,
fill: '#000000'
});
self.addChild(self.wordText);
self.interactive = true;
self.isSelected = false;
self.linkedTo = null;
self.setup = function (word, posX, posY) {
self.wordText.setText(word);
self.x = posX;
self.y = posY;
self.word = word;
};
self.select = function () {
self.isSelected = true;
var highlight = LK.getAsset('selectedWord', {
anchorX: 0.5,
anchorY: 0.5
});
self.addChildAt(highlight, 0);
tween(self, {
alpha: 0.8
}, {
duration: 200
});
};
self.deselect = function () {
self.isSelected = false;
if (self.children.length > 1) {
self.removeChildAt(0);
}
tween(self, {
alpha: 1
}, {
duration: 200
});
};
return self;
});
var UMLClassDiagram = Container.expand(function () {
var self = Container.call(this);
self.className = '';
self.properties = [];
self.methods = [];
self.zones = [];
self.addClassBox = function (classNameStr, properties, methods) {
self.className = classNameStr;
self.properties = properties;
self.methods = methods;
var classGraphics = LK.getAsset('classBox', {
anchorX: 0.5,
anchorY: 0.5
});
self.addChild(classGraphics);
var titleText = new Text2(classNameStr, {
size: 28,
fill: '#000000'
});
titleText.anchor.set(0.5, 0);
titleText.y = -80;
self.addChild(titleText);
var yOffset = -50;
for (var i = 0; i < properties.length; i++) {
var propText = new Text2('- ' + properties[i].name + ': ' + properties[i].type, {
size: 20,
fill: '#333333'
});
propText.anchor.set(0.5, 0);
propText.y = yOffset;
self.addChild(propText);
var zone = new DiagramZone();
zone.setup(properties[i].name, 0, yOffset, 'prop_' + i, properties[i].name);
self.zones.push(zone);
yOffset += 25;
}
yOffset += 10;
for (var j = 0; j < methods.length; j++) {
var methodText = new Text2('+ ' + methods[j].name + '()', {
size: 20,
fill: '#333333'
});
methodText.anchor.set(0.5, 0);
methodText.y = yOffset;
self.addChild(methodText);
var mzone = new DiagramZone();
mzone.setup(methods[j].name, 0, yOffset, 'method_' + j, methods[j].name);
self.zones.push(mzone);
yOffset += 25;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xffffff
});
/****
* Game Code
****/
/****
* Game Data
****/
var levelData = [{
name: 'Class Basics',
documents: [{
scenario: 'A bank account has a balance and an owner name. It can deposit money and withdraw money.',
classData: {
name: 'BankAccount',
properties: [{
name: 'balance',
type: 'float'
}, {
name: 'owner',
type: 'string'
}],
methods: [{
name: 'deposit',
type: 'void'
}, {
name: 'withdraw',
type: 'void'
}]
},
errors: []
}, {
scenario: 'A car has color, model, and year. It can start engine and stop engine.',
classData: {
name: 'Car',
properties: [{
name: 'color',
type: 'string'
}, {
name: 'model',
type: 'string'
}],
methods: [{
name: 'startEngine',
type: 'void'
}, {
name: 'stopEngine',
type: 'void'
}]
},
errors: [{
type: 'missingProperty',
expected: 'year'
}]
}, {
scenario: 'A student has name, ID, and grade. They can submit assignment and get grades.',
classData: {
name: 'Student',
properties: [{
name: 'name',
type: 'string'
}, {
name: 'studentId',
type: 'int'
}],
methods: [{
name: 'submitAssignment',
type: 'void'
}, {
name: 'getGrades',
type: 'array'
}]
},
errors: [{
type: 'missingProperty',
expected: 'grade'
}]
}, {
scenario: 'A book has title, author, and ISBN. It can be borrowed and returned.',
classData: {
name: 'Book',
properties: [{
name: 'title',
type: 'string'
}, {
name: 'author',
type: 'string'
}, {
name: 'isbn',
type: 'string'
}],
methods: [{
name: 'borrow',
type: 'void'
}, {
name: 'return',
type: 'void'
}]
},
errors: []
}, {
scenario: 'A person has first name, last name, and age. They can walk and eat.',
classData: {
name: 'Person',
properties: [{
name: 'firstName',
type: 'string'
}, {
name: 'lastName',
type: 'string'
}],
methods: [{
name: 'walk',
type: 'void'
}, {
name: 'eat',
type: 'void'
}]
},
errors: [{
type: 'missingProperty',
expected: 'age'
}]
}]
}, {
name: 'Access Modifiers',
documents: [{
scenario: 'A password manager stores encrypted passwords (private). Users can set and verify passwords (public).',
classData: {
name: 'PasswordManager',
properties: [{
name: '- encryptedPassword',
type: 'string'
}, {
name: '+ userId',
type: 'int'
}],
methods: [{
name: '+ setPassword',
type: 'void'
}, {
name: '+ verifyPassword',
type: 'boolean'
}]
},
errors: []
}, {
scenario: 'A database connection should have private host and port. Public methods connect and disconnect.',
classData: {
name: 'DatabaseConnection',
properties: [{
name: '- host',
type: 'string'
}, {
name: '+ port',
type: 'int'
}],
methods: [{
name: '+ connect',
type: 'void'
}, {
name: '+ disconnect',
type: 'void'
}]
},
errors: [{
type: 'wrongAccessModifier',
expected: 'port should be private'
}]
}, {
scenario: 'A calculator performs private validation on inputs. Public methods add, subtract, and multiply.',
classData: {
name: 'Calculator',
properties: [],
methods: [{
name: '+ add',
type: 'double'
}, {
name: '+ subtract',
type: 'double'
}, {
name: '+ multiply',
type: 'double'
}]
},
errors: [{
type: 'missingMethod',
expected: 'validate method should be private'
}]
}, {
scenario: 'A bank account has private balance and private PIN. Public methods deposit and withdraw.',
classData: {
name: 'SecureAccount',
properties: [{
name: '- balance',
type: 'float'
}, {
name: '- pin',
type: 'string'
}],
methods: [{
name: '+ deposit',
type: 'void'
}, {
name: '+ withdraw',
type: 'void'
}]
},
errors: []
}, {
scenario: 'A user profile has public name and email. Password is private. Can update profile publicly.',
classData: {
name: 'UserProfile',
properties: [{
name: '+ name',
type: 'string'
}, {
name: '+ email',
type: 'string'
}, {
name: '- password',
type: 'string'
}],
methods: [{
name: '+ updateProfile',
type: 'void'
}]
},
errors: []
}]
}, {
name: 'Inheritance & Multiplicity',
documents: [{
scenario: 'A company has many employees. Each employee has a name and salary. Employees can work.',
classData: {
name: 'Company',
properties: [{
name: 'name',
type: 'string'
}, {
name: 'employees',
type: 'list<Employee>'
}],
methods: [{
name: 'addEmployee',
type: 'void'
}, {
name: 'getEmployees',
type: 'list<Employee>'
}]
},
errors: []
}, {
scenario: 'A vehicle is a parent class. Cars and motorcycles are vehicles. Vehicles have speed and can stop.',
classData: {
name: 'Vehicle',
properties: [{
name: 'speed',
type: 'double'
}],
methods: [{
name: 'accelerate',
type: 'void'
}, {
name: 'stop',
type: 'void'
}]
},
errors: []
}, {
scenario: 'A library has many books. A book belongs to one library. Books have ISBN and title.',
classData: {
name: 'Library',
properties: [{
name: 'name',
type: 'string'
}, {
name: 'books',
type: 'list<Book>'
}],
methods: [{
name: 'addBook',
type: 'void'
}]
},
errors: [{
type: 'missingProperty',
expected: 'location'
}]
}, {
scenario: 'An animal eats and sleeps. Dogs and cats are animals that can bark and meow.',
classData: {
name: 'Animal',
properties: [{
name: 'name',
type: 'string'
}],
methods: [{
name: 'eat',
type: 'void'
}, {
name: 'sleep',
type: 'void'
}]
},
errors: []
}, {
scenario: 'A school has many students and many teachers. Students study. Teachers teach.',
classData: {
name: 'School',
properties: [{
name: 'name',
type: 'string'
}, {
name: 'students',
type: 'list<Student>'
}, {
name: 'teachers',
type: 'list<Teacher>'
}],
methods: [{
name: 'addStudent',
type: 'void'
}, {
name: 'addTeacher',
type: 'void'
}]
},
errors: []
}]
}];
/****
* Game Variables
****/
var currentLevel = storage.level - 1;
var currentDocIndex = storage.currentDocIndex;
var currentDocPair = null;
var selectedWord = null;
var score = LK.getScore();
var documents = [];
var approveButton = null;
var rejectButton = null;
var rulePanelVisible = false;
var rulePanel = null;
/****
* Helper Functions
****/
function initializeLevel() {
var level = levelData[currentLevel];
documents = [];
for (var i = 0; i < level.documents.length; i++) {
var doc = new DocumentPair();
doc.setup(level.documents[i].scenario, level.documents[i].classData, level.documents[i].errors);
documents.push(doc);
}
loadDocument(0);
}
function loadDocument(docIndex) {
if (docIndex >= documents.length) {
showDayComplete();
return;
}
currentDocIndex = docIndex;
currentDocPair = documents[docIndex];
game.removeChildren();
game.addChild(currentDocPair);
createControlUI();
createRulePanel();
}
function createControlUI() {
var approveGraphics = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
approveButton = new Container();
approveButton.addChild(approveGraphics);
approveButton.interactive = true;
var approveText = new Text2('APPROVE', {
size: 32,
fill: '#ffffff'
});
approveText.anchor.set(0.5, 0.5);
approveButton.addChild(approveText);
approveButton.x = 1850;
approveButton.y = 1400;
game.addChild(approveButton);
var rejectGraphics = LK.getAsset('rejectButton', {
anchorX: 0.5,
anchorY: 0.5
});
rejectButton = new Container();
rejectButton.addChild(rejectGraphics);
rejectButton.interactive = true;
var rejectText = new Text2('REJECT', {
size: 32,
fill: '#ffffff'
});
rejectText.anchor.set(0.5, 0.5);
rejectButton.addChild(rejectText);
rejectButton.x = 1850;
rejectButton.y = 1550;
game.addChild(rejectButton);
var scoreText = new Text2('Score: ' + score, {
size: 40,
fill: '#000000'
});
scoreText.anchor.set(0, 0);
scoreText.x = 50;
scoreText.y = 1900;
game.addChild(scoreText);
scoreText.scoreDisplay = true;
var docCountText = new Text2('Document ' + (currentDocIndex + 1) + ' / ' + documents.length, {
size: 32,
fill: '#666666'
});
docCountText.anchor.set(0, 0);
docCountText.x = 50;
docCountText.y = 1960;
game.addChild(docCountText);
}
function createRulePanel() {
var level = levelData[currentLevel];
var ruleGraphics = LK.getAsset('rulePanel', {
anchorX: 0,
anchorY: 0
});
rulePanel = new Container();
rulePanel.addChild(ruleGraphics);
rulePanel.x = 1050;
rulePanel.y = 50;
var ruleTitle = new Text2('RULES', {
size: 28,
fill: '#333333'
});
ruleTitle.anchor.set(0.5, 0);
ruleTitle.x = 150;
ruleTitle.y = 20;
rulePanel.addChild(ruleTitle);
var ruleText = '';
if (currentLevel === 0) {
ruleText = 'L1: Basic Classes\n\nVerify properties match the description\n\nVerify methods match the description';
} else if (currentLevel === 1) {
ruleText = 'L2: Access Modifiers\n\n+ = public\n- = private\n\nData should be private\nMethods should be public';
} else {
ruleText = 'L3: Inheritance\n\nParent classes define shared behavior\n\nMultiplicity: one-to-many relationships';
}
var ruleContentText = new Text2(ruleText, {
size: 18,
fill: '#555555'
});
ruleContentText.anchor.set(0, 0);
ruleContentText.x = 20;
ruleContentText.y = 70;
ruleContentText.wordWrap = true;
ruleContentText.wordWrapWidth = 260;
rulePanel.addChild(ruleContentText);
game.addChild(rulePanel);
}
function showDayComplete() {
game.removeChildren();
var bgBox = LK.getAsset('diagramBox', {
anchorX: 0.5,
anchorY: 0.5
});
var bgContainer = new Container();
bgContainer.addChild(bgBox);
bgContainer.x = 1024;
bgContainer.y = 1366;
game.addChild(bgContainer);
var dayCompleteText = new Text2('DAY COMPLETE', {
size: 64,
fill: '#333333'
});
dayCompleteText.anchor.set(0.5, 0.5);
dayCompleteText.x = 1024;
dayCompleteText.y = 1100;
game.addChild(dayCompleteText);
var finalScoreText = new Text2('Final Score: ' + score, {
size: 48,
fill: '#000000'
});
finalScoreText.anchor.set(0.5, 0.5);
finalScoreText.x = 1024;
finalScoreText.y = 1366;
game.addChild(finalScoreText);
var nextLevelText = '';
if (currentLevel < levelData.length - 1) {
nextLevelText = 'Next: ' + levelData[currentLevel + 1].name;
storage.level = currentLevel + 2;
storage.currentDocIndex = 0;
} else {
nextLevelText = 'Game Complete!';
}
var nextText = new Text2(nextLevelText, {
size: 36,
fill: '#4CAF50'
});
nextText.anchor.set(0.5, 0.5);
nextText.x = 1024;
nextText.y = 1600;
game.addChild(nextText);
}
function processDecision(isApproved) {
var hasErrors = currentDocPair.errors.length > 0;
var isCorrect = false;
if (isApproved) {
isCorrect = !hasErrors;
} else {
isCorrect = hasErrors;
}
if (isCorrect) {
score += 10;
LK.getSound('success').play();
} else {
score -= 10;
LK.getSound('error').play();
}
LK.setScore(score);
loadDocument(currentDocIndex + 1);
}
/****
* Game Update
****/
game.update = function () {
//Game loop updates handled by LK
};
/****
* Event Handlers
****/
game.down = function (x, y, obj) {
if (approveButton && obj === approveButton.children[0]) {
processDecision(true);
} else if (rejectButton && obj === rejectButton.children[0]) {
processDecision(false);
}
};
game.move = function (x, y, obj) {
//Handle drag operations if needed
};
game.up = function (x, y, obj) {
//Handle release operations if needed
};
initializeLevel(); /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
level: 1,
currentDocIndex: 0
});
/****
* Classes
****/
var DiagramZone = Container.expand(function () {
var self = Container.call(this);
self.zoneText = new Text2('zone', {
size: 24,
fill: '#333333'
});
self.addChild(self.zoneText);
self.interactive = true;
self.linkedFrom = null;
self.zoneId = '';
self.correctWord = '';
self.setup = function (label, posX, posY, zoneId, correctWord) {
self.zoneText.setText(label);
self.x = posX;
self.y = posY;
self.zoneId = zoneId;
self.correctWord = correctWord;
};
self.highlight = function () {
tween(self, {
alpha: 0.6
}, {
duration: 150
});
};
self.unhighlight = function () {
tween(self, {
alpha: 1
}, {
duration: 150
});
};
return self;
});
var DocumentPair = Container.expand(function () {
var self = Container.call(this);
self.scenario = '';
self.umlDiagram = null;
self.errors = [];
self.selectableWords = [];
self.links = [];
self.isApproved = false;
self.isRejected = false;
self.setup = function (scenario, classData, errors) {
self.scenario = scenario;
self.errors = errors;
var textBox = LK.getAsset('textBox', {
anchorX: 0,
anchorY: 0
});
textBox.x = 50;
textBox.y = 50;
self.addChild(textBox);
var scenarioText = new Text2(scenario, {
size: 24,
fill: '#333333'
});
scenarioText.anchor.set(0, 0);
scenarioText.x = 70;
scenarioText.y = 70;
scenarioText.wordWrap = true;
scenarioText.wordWrapWidth = 850;
self.addChild(scenarioText);
var diagramBox = LK.getAsset('diagramBox', {
anchorX: 0,
anchorY: 0
});
diagramBox.x = 50;
diagramBox.y = 480;
self.addChild(diagramBox);
self.umlDiagram = new UMLClassDiagram();
self.umlDiagram.x = 250;
self.umlDiagram.y = 600;
self.umlDiagram.addClassBox(classData.name, classData.properties, classData.methods);
self.addChild(self.umlDiagram);
};
self.approve = function () {
self.isApproved = true;
LK.getSound('stamp').play();
var hasErrors = self.errors.length > 0 && !self.hasFoundAllErrors();
return !hasErrors;
};
self.reject = function () {
self.isRejected = true;
LK.getSound('stamp').play();
var hasErrors = self.errors.length > 0;
return hasErrors;
};
self.hasFoundAllErrors = function () {
return self.links.length >= self.errors.length;
};
return self;
});
var SelectableWord = Container.expand(function () {
var self = Container.call(this);
self.wordText = new Text2('word', {
size: 32,
fill: '#000000'
});
self.addChild(self.wordText);
self.interactive = true;
self.isSelected = false;
self.linkedTo = null;
self.setup = function (word, posX, posY) {
self.wordText.setText(word);
self.x = posX;
self.y = posY;
self.word = word;
};
self.select = function () {
self.isSelected = true;
var highlight = LK.getAsset('selectedWord', {
anchorX: 0.5,
anchorY: 0.5
});
self.addChildAt(highlight, 0);
tween(self, {
alpha: 0.8
}, {
duration: 200
});
};
self.deselect = function () {
self.isSelected = false;
if (self.children.length > 1) {
self.removeChildAt(0);
}
tween(self, {
alpha: 1
}, {
duration: 200
});
};
return self;
});
var UMLClassDiagram = Container.expand(function () {
var self = Container.call(this);
self.className = '';
self.properties = [];
self.methods = [];
self.zones = [];
self.addClassBox = function (classNameStr, properties, methods) {
self.className = classNameStr;
self.properties = properties;
self.methods = methods;
var classGraphics = LK.getAsset('classBox', {
anchorX: 0.5,
anchorY: 0.5
});
self.addChild(classGraphics);
var titleText = new Text2(classNameStr, {
size: 28,
fill: '#000000'
});
titleText.anchor.set(0.5, 0);
titleText.y = -80;
self.addChild(titleText);
var yOffset = -50;
for (var i = 0; i < properties.length; i++) {
var propText = new Text2('- ' + properties[i].name + ': ' + properties[i].type, {
size: 20,
fill: '#333333'
});
propText.anchor.set(0.5, 0);
propText.y = yOffset;
self.addChild(propText);
var zone = new DiagramZone();
zone.setup(properties[i].name, 0, yOffset, 'prop_' + i, properties[i].name);
self.zones.push(zone);
yOffset += 25;
}
yOffset += 10;
for (var j = 0; j < methods.length; j++) {
var methodText = new Text2('+ ' + methods[j].name + '()', {
size: 20,
fill: '#333333'
});
methodText.anchor.set(0.5, 0);
methodText.y = yOffset;
self.addChild(methodText);
var mzone = new DiagramZone();
mzone.setup(methods[j].name, 0, yOffset, 'method_' + j, methods[j].name);
self.zones.push(mzone);
yOffset += 25;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0xffffff
});
/****
* Game Code
****/
/****
* Game Data
****/
var levelData = [{
name: 'Class Basics',
documents: [{
scenario: 'A bank account has a balance and an owner name. It can deposit money and withdraw money.',
classData: {
name: 'BankAccount',
properties: [{
name: 'balance',
type: 'float'
}, {
name: 'owner',
type: 'string'
}],
methods: [{
name: 'deposit',
type: 'void'
}, {
name: 'withdraw',
type: 'void'
}]
},
errors: []
}, {
scenario: 'A car has color, model, and year. It can start engine and stop engine.',
classData: {
name: 'Car',
properties: [{
name: 'color',
type: 'string'
}, {
name: 'model',
type: 'string'
}],
methods: [{
name: 'startEngine',
type: 'void'
}, {
name: 'stopEngine',
type: 'void'
}]
},
errors: [{
type: 'missingProperty',
expected: 'year'
}]
}, {
scenario: 'A student has name, ID, and grade. They can submit assignment and get grades.',
classData: {
name: 'Student',
properties: [{
name: 'name',
type: 'string'
}, {
name: 'studentId',
type: 'int'
}],
methods: [{
name: 'submitAssignment',
type: 'void'
}, {
name: 'getGrades',
type: 'array'
}]
},
errors: [{
type: 'missingProperty',
expected: 'grade'
}]
}, {
scenario: 'A book has title, author, and ISBN. It can be borrowed and returned.',
classData: {
name: 'Book',
properties: [{
name: 'title',
type: 'string'
}, {
name: 'author',
type: 'string'
}, {
name: 'isbn',
type: 'string'
}],
methods: [{
name: 'borrow',
type: 'void'
}, {
name: 'return',
type: 'void'
}]
},
errors: []
}, {
scenario: 'A person has first name, last name, and age. They can walk and eat.',
classData: {
name: 'Person',
properties: [{
name: 'firstName',
type: 'string'
}, {
name: 'lastName',
type: 'string'
}],
methods: [{
name: 'walk',
type: 'void'
}, {
name: 'eat',
type: 'void'
}]
},
errors: [{
type: 'missingProperty',
expected: 'age'
}]
}]
}, {
name: 'Access Modifiers',
documents: [{
scenario: 'A password manager stores encrypted passwords (private). Users can set and verify passwords (public).',
classData: {
name: 'PasswordManager',
properties: [{
name: '- encryptedPassword',
type: 'string'
}, {
name: '+ userId',
type: 'int'
}],
methods: [{
name: '+ setPassword',
type: 'void'
}, {
name: '+ verifyPassword',
type: 'boolean'
}]
},
errors: []
}, {
scenario: 'A database connection should have private host and port. Public methods connect and disconnect.',
classData: {
name: 'DatabaseConnection',
properties: [{
name: '- host',
type: 'string'
}, {
name: '+ port',
type: 'int'
}],
methods: [{
name: '+ connect',
type: 'void'
}, {
name: '+ disconnect',
type: 'void'
}]
},
errors: [{
type: 'wrongAccessModifier',
expected: 'port should be private'
}]
}, {
scenario: 'A calculator performs private validation on inputs. Public methods add, subtract, and multiply.',
classData: {
name: 'Calculator',
properties: [],
methods: [{
name: '+ add',
type: 'double'
}, {
name: '+ subtract',
type: 'double'
}, {
name: '+ multiply',
type: 'double'
}]
},
errors: [{
type: 'missingMethod',
expected: 'validate method should be private'
}]
}, {
scenario: 'A bank account has private balance and private PIN. Public methods deposit and withdraw.',
classData: {
name: 'SecureAccount',
properties: [{
name: '- balance',
type: 'float'
}, {
name: '- pin',
type: 'string'
}],
methods: [{
name: '+ deposit',
type: 'void'
}, {
name: '+ withdraw',
type: 'void'
}]
},
errors: []
}, {
scenario: 'A user profile has public name and email. Password is private. Can update profile publicly.',
classData: {
name: 'UserProfile',
properties: [{
name: '+ name',
type: 'string'
}, {
name: '+ email',
type: 'string'
}, {
name: '- password',
type: 'string'
}],
methods: [{
name: '+ updateProfile',
type: 'void'
}]
},
errors: []
}]
}, {
name: 'Inheritance & Multiplicity',
documents: [{
scenario: 'A company has many employees. Each employee has a name and salary. Employees can work.',
classData: {
name: 'Company',
properties: [{
name: 'name',
type: 'string'
}, {
name: 'employees',
type: 'list<Employee>'
}],
methods: [{
name: 'addEmployee',
type: 'void'
}, {
name: 'getEmployees',
type: 'list<Employee>'
}]
},
errors: []
}, {
scenario: 'A vehicle is a parent class. Cars and motorcycles are vehicles. Vehicles have speed and can stop.',
classData: {
name: 'Vehicle',
properties: [{
name: 'speed',
type: 'double'
}],
methods: [{
name: 'accelerate',
type: 'void'
}, {
name: 'stop',
type: 'void'
}]
},
errors: []
}, {
scenario: 'A library has many books. A book belongs to one library. Books have ISBN and title.',
classData: {
name: 'Library',
properties: [{
name: 'name',
type: 'string'
}, {
name: 'books',
type: 'list<Book>'
}],
methods: [{
name: 'addBook',
type: 'void'
}]
},
errors: [{
type: 'missingProperty',
expected: 'location'
}]
}, {
scenario: 'An animal eats and sleeps. Dogs and cats are animals that can bark and meow.',
classData: {
name: 'Animal',
properties: [{
name: 'name',
type: 'string'
}],
methods: [{
name: 'eat',
type: 'void'
}, {
name: 'sleep',
type: 'void'
}]
},
errors: []
}, {
scenario: 'A school has many students and many teachers. Students study. Teachers teach.',
classData: {
name: 'School',
properties: [{
name: 'name',
type: 'string'
}, {
name: 'students',
type: 'list<Student>'
}, {
name: 'teachers',
type: 'list<Teacher>'
}],
methods: [{
name: 'addStudent',
type: 'void'
}, {
name: 'addTeacher',
type: 'void'
}]
},
errors: []
}]
}];
/****
* Game Variables
****/
var currentLevel = storage.level - 1;
var currentDocIndex = storage.currentDocIndex;
var currentDocPair = null;
var selectedWord = null;
var score = LK.getScore();
var documents = [];
var approveButton = null;
var rejectButton = null;
var rulePanelVisible = false;
var rulePanel = null;
/****
* Helper Functions
****/
function initializeLevel() {
var level = levelData[currentLevel];
documents = [];
for (var i = 0; i < level.documents.length; i++) {
var doc = new DocumentPair();
doc.setup(level.documents[i].scenario, level.documents[i].classData, level.documents[i].errors);
documents.push(doc);
}
loadDocument(0);
}
function loadDocument(docIndex) {
if (docIndex >= documents.length) {
showDayComplete();
return;
}
currentDocIndex = docIndex;
currentDocPair = documents[docIndex];
game.removeChildren();
game.addChild(currentDocPair);
createControlUI();
createRulePanel();
}
function createControlUI() {
var approveGraphics = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5
});
approveButton = new Container();
approveButton.addChild(approveGraphics);
approveButton.interactive = true;
var approveText = new Text2('APPROVE', {
size: 32,
fill: '#ffffff'
});
approveText.anchor.set(0.5, 0.5);
approveButton.addChild(approveText);
approveButton.x = 1850;
approveButton.y = 1400;
game.addChild(approveButton);
var rejectGraphics = LK.getAsset('rejectButton', {
anchorX: 0.5,
anchorY: 0.5
});
rejectButton = new Container();
rejectButton.addChild(rejectGraphics);
rejectButton.interactive = true;
var rejectText = new Text2('REJECT', {
size: 32,
fill: '#ffffff'
});
rejectText.anchor.set(0.5, 0.5);
rejectButton.addChild(rejectText);
rejectButton.x = 1850;
rejectButton.y = 1550;
game.addChild(rejectButton);
var scoreText = new Text2('Score: ' + score, {
size: 40,
fill: '#000000'
});
scoreText.anchor.set(0, 0);
scoreText.x = 50;
scoreText.y = 1900;
game.addChild(scoreText);
scoreText.scoreDisplay = true;
var docCountText = new Text2('Document ' + (currentDocIndex + 1) + ' / ' + documents.length, {
size: 32,
fill: '#666666'
});
docCountText.anchor.set(0, 0);
docCountText.x = 50;
docCountText.y = 1960;
game.addChild(docCountText);
}
function createRulePanel() {
var level = levelData[currentLevel];
var ruleGraphics = LK.getAsset('rulePanel', {
anchorX: 0,
anchorY: 0
});
rulePanel = new Container();
rulePanel.addChild(ruleGraphics);
rulePanel.x = 1050;
rulePanel.y = 50;
var ruleTitle = new Text2('RULES', {
size: 28,
fill: '#333333'
});
ruleTitle.anchor.set(0.5, 0);
ruleTitle.x = 150;
ruleTitle.y = 20;
rulePanel.addChild(ruleTitle);
var ruleText = '';
if (currentLevel === 0) {
ruleText = 'L1: Basic Classes\n\nVerify properties match the description\n\nVerify methods match the description';
} else if (currentLevel === 1) {
ruleText = 'L2: Access Modifiers\n\n+ = public\n- = private\n\nData should be private\nMethods should be public';
} else {
ruleText = 'L3: Inheritance\n\nParent classes define shared behavior\n\nMultiplicity: one-to-many relationships';
}
var ruleContentText = new Text2(ruleText, {
size: 18,
fill: '#555555'
});
ruleContentText.anchor.set(0, 0);
ruleContentText.x = 20;
ruleContentText.y = 70;
ruleContentText.wordWrap = true;
ruleContentText.wordWrapWidth = 260;
rulePanel.addChild(ruleContentText);
game.addChild(rulePanel);
}
function showDayComplete() {
game.removeChildren();
var bgBox = LK.getAsset('diagramBox', {
anchorX: 0.5,
anchorY: 0.5
});
var bgContainer = new Container();
bgContainer.addChild(bgBox);
bgContainer.x = 1024;
bgContainer.y = 1366;
game.addChild(bgContainer);
var dayCompleteText = new Text2('DAY COMPLETE', {
size: 64,
fill: '#333333'
});
dayCompleteText.anchor.set(0.5, 0.5);
dayCompleteText.x = 1024;
dayCompleteText.y = 1100;
game.addChild(dayCompleteText);
var finalScoreText = new Text2('Final Score: ' + score, {
size: 48,
fill: '#000000'
});
finalScoreText.anchor.set(0.5, 0.5);
finalScoreText.x = 1024;
finalScoreText.y = 1366;
game.addChild(finalScoreText);
var nextLevelText = '';
if (currentLevel < levelData.length - 1) {
nextLevelText = 'Next: ' + levelData[currentLevel + 1].name;
storage.level = currentLevel + 2;
storage.currentDocIndex = 0;
} else {
nextLevelText = 'Game Complete!';
}
var nextText = new Text2(nextLevelText, {
size: 36,
fill: '#4CAF50'
});
nextText.anchor.set(0.5, 0.5);
nextText.x = 1024;
nextText.y = 1600;
game.addChild(nextText);
}
function processDecision(isApproved) {
var hasErrors = currentDocPair.errors.length > 0;
var isCorrect = false;
if (isApproved) {
isCorrect = !hasErrors;
} else {
isCorrect = hasErrors;
}
if (isCorrect) {
score += 10;
LK.getSound('success').play();
} else {
score -= 10;
LK.getSound('error').play();
}
LK.setScore(score);
loadDocument(currentDocIndex + 1);
}
/****
* Game Update
****/
game.update = function () {
//Game loop updates handled by LK
};
/****
* Event Handlers
****/
game.down = function (x, y, obj) {
if (approveButton && obj === approveButton.children[0]) {
processDecision(true);
} else if (rejectButton && obj === rejectButton.children[0]) {
processDecision(false);
}
};
game.move = function (x, y, obj) {
//Handle drag operations if needed
};
game.up = function (x, y, obj) {
//Handle release operations if needed
};
initializeLevel();