logo
Menu
The Legend of AWS Warrior: A Free Opensource 3D RPG Adventure Game with Generative AI for learning AWS

The Legend of AWS Warrior: A Free Opensource 3D RPG Adventure Game with Generative AI for learning AWS

"The Legend of AWS Warrior" is an innovative approach to learning AWS through 3D RPG gaming at Hong Kong Institute of Information Technology (HKIIT). By integrating various AWS services such as Amazon Bedrock, AWS Lambda, Amazon S3, and Amazon DynamoDB into an AWS SAM serverless application, it offers a practical, hands-on experience with AWS Academy.

Published May 3, 2024
Last Modified May 6, 2024

Introduction

"The Legend of AWS Warrior" is an innovative approach to learning AWS through gaming at Hong Kong Institute of Information Technology (HKIIT) @ IVE(Lee Wai Lee). By integrating various AWS services such as AWS Lambda, Amazon S3, and Amazon DynamoDB into a AWS SAM serverless application, it offers a practical, hands-on experience. It's designed to help students using the AWS Academy Learner Lab and AWS Educate Lab to engage with cloud computing concepts in a fun and interactive way through Amazon Bedrock. The game allows for customization, enabling educators and learners to add new unit tests and expand the learning framework. This project not only makes learning about AWS more accessible but also encourages the development of practical skills in a simulated environment.

Demo

Student interacts with a virtual NPC (non-player character) girl to receive instructions generated by Amazon Bedrock. Their mission: to defeat a digital monster, an action that cleverly triggers a unit test in AWS Lambda. This gamified approach makes learning about cloud infrastructure engaging and fun.
First and foremost, we extend our gratitude to SimonDev. The game is a modification of his repository, Quick_3D_RPG.

Background

AWS Academy Learner Lab is a long-running hands-on lab environment where educators can bring their own assignments and invite their students to get experience using select AWS Services. Due to limitations imposed by AWS, every student will be provided with an AWS account with US$50. However, this account has certain restrictions, including the disabling of certain AWS services such as Auroa serverless. Additionally, students will not have access to modify IAM permissions. As a result, assignments should not rely on SQL databases as a component.
This AWS account provides students with the ability to generate 4-hour IAM session tokens for the AWS SDK or AWS CLI. These tokens are used by our backend system to assess and validate the AWS resources and configurations of the students.

Architecture

The Legend of AWS Warrior Architecture
The Legend of AWS Warrior Architecture
The system consists of two parts: a static website and a web API.

Static website

1. ReactJS Site (index.html): The static website is built using ReactJS. It includes a landing page for students to submit their IAM session key and check their marks. The hash key is from the teacher by email, and it is a unique key.
After 4 hours, students need to resubmit the key again.
2. 3D Game Site (game.html): The 3D game site is hosted in an Amazon S3 bucket.
Students find an NPC and click on it. The NPC will give them an AWS task or an encouraging message with Amazon Bedrock.
Students need to follow finish the task in their AWS Academy Learner Lab account and kill a monster to check the task result.
3. Amazon CloudFront Web Distribution: Amazon CloudFront is a content delivery network (CDN) service that provides fast and reliable delivery of web content. It is used to enforce access control through Origin Access Control and optimize the performance of the static website.

Web API

This is a C# .NET 8 WebAPI App Lambda with Amazon API Gateway proxy integration, created from the default template app using the “sam init” command.

ProjectTestsLib

This library contains NUnit tests that utilize the AWS .NET SDK to query students’ AWS account resources and settings. The tests then assert the expected values.
  1. The GameClass attribute defines the order and timeout for game-related tasks.
  2. The GameTask attribute provides the raw instructions, time limit, and reward for specific tasks. Although the time limit is not currently in use, in the next version, the system will calculate the time between students receiving the task and completing it. This adjustment will allow us to fine-tune rewards based on completion time.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
using Amazon.EC2;
using Amazon.EC2.Model;
using NUnit.Framework;
using ProjectTestsLib.Helper;
namespace ProjectTestsLib;

[GameClass(2), CancelAfter(Constants.Timeout), Order(2)]
public class T02_VpcTest: AwsTest
{
private AmazonEC2Client? Ec2Client { get; set; }
private string? VpcId { get; set; }

[SetUp]
public new void Setup()
{
base.Setup();
Ec2Client = new AmazonEC2Client(Credential);
VpcId = QueryHelper.GetVpcId(Ec2Client);
}

[TearDown]
public void TearDown()
{
Ec2Client?.Dispose();
}

[GameTask("Create a VPC with CIDR 10.0.0.0/16 and name it as 'Cloud Project VPC'.", 2, 10)]
[Test, Order(1)]
public async Task Test01_VpcExist()
{
var describeVpcsRequest = new DescribeVpcsRequest();
describeVpcsRequest.Filters.Add(new Filter("tag:Name", ["Cloud Project VPC"]));
var describeVpcsResponse = await Ec2Client!.DescribeVpcsAsync(describeVpcsRequest);

Assert.That(describeVpcsResponse.Vpcs, Has.Count.EqualTo(1));
}

[GameTask("In 'Cloud Project VPC', Create 4 subnets with CIDR '10.0.0.0/24','10.0.1.0/24','10.0.4.0/22','10.0.8.0/22'.", 2, 10)]
[Test, Order(2)]
public async Task Test02_VpcOf4Subnets()
{
DescribeSubnetsRequest describeSubnetsRequest = new();
describeSubnetsRequest.Filters.Add(new Filter("vpc-id", [VpcId]));
var describeSubnetsResponse = await Ec2Client!.DescribeSubnetsAsync(describeSubnetsRequest);
Assert.That(describeSubnetsResponse.Subnets.Count(), Is.EqualTo(4));
var expectedCidrAddresses = new string[] { "10.0.0.0/24", "10.0.1.0/24", "10.0.4.0/22", "10.0.8.0/22" };
List<string> acturalCidrAddresses = describeSubnetsResponse.Subnets.Select(c => c.CidrBlock).ToList();
Assert.That(acturalCidrAddresses, Is.EquivalentTo(expectedCidrAddresses));
}
}

ServerlessAPI

This is a Microsoft Asp Net Core Web Application and it uses the hash key for authentication.
AwsAccountController
It manages AWS IAM session tokens. Behind the scenes, it checks and saves the email, AWS account number, and AWS IAM session token in an AwsAccountTable (Table will be refer to Amazon DynamoDB table).
It will block student from sharing AWS account to get mark.
GameController
This function is triggered when students click on an NPC. It has two possible behaviors:
  1. Random Message Generation: Sometimes, it returns a randomly generated message using the Amazon Bedrock model with the ID amazon.titan-text-express-v1.
  2. Task Retrieval: If not generating a random message, it checks the PassedTestTable in Amazon DynamoDB. If there’s an incomplete task, it returns the details of the next task to the student. To extract task details, the function uses reflection on the GameClassAttribute and GameTaskAttribute.
Additionally, before returning the instruction, there’s a 70% chance that it will be rephrased by Amazon Bedrock.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
if (new Random().NextDouble() < 0.5)
{
return new JsonResult(await awsBedrock.RandomNPCConversation());
}

var passedTests = await dynamoDB.GetPassedTestNames(user.Email);
logger.LogInformation($"Passed tests: {string.Join(", ", passedTests)}");

var tasks = GetTasksJson();
var filteredTasks = tasks.Where(t => !t.Tests.All(passedTests.Contains));
if (string.IsNullOrEmpty(mode))
{
return new JsonResult(filteredTasks);
}
var t = filteredTasks.Take(1).ToArray();
if (new Random().NextDouble() < 0.7)
{
t[0].Instruction = await awsBedrock.RewriteInstruction(t[0].Instruction);
}
return new JsonResult(t);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80

using System.Text.Json.Nodes;
using Amazon;
using Amazon.BedrockRuntime;
using Amazon.BedrockRuntime.Model;
using Amazon.Util;

namespace ServerlessAPI.Helper;
public class AwsBedrock
{
private readonly ILogger<AwsBedrock> logger;

public AwsBedrock(ILogger<AwsBedrock> logger)
{
this.logger = logger;
}

public async Task<string> RandomNPCConversation(){
string prompt = """
Write a short sentence in less than 20 words to the AWS warrior to encorage him to fight the monster.
"
"";
return await InvokeTitanTextG1Async(prompt);
}

public async Task<string> RewriteInstruction(string instruction)
{
string prompt = """
Rewrite the following instruction for a game NPC which is girl in age 20 and ask for help from the AWS warrior:

"
"" + instruction + @"

"
;

return await InvokeTitanTextG1Async(prompt);
}
private async Task<string> InvokeTitanTextG1Async(string prompt)
{
string titanTextG1ModelId = "amazon.titan-text-express-v1";
AmazonBedrockRuntimeClient client = new(RegionEndpoint.USEast1);
string payload = new JsonObject()
{
{ "inputText", prompt },
{ "textGenerationConfig", new JsonObject()
{
{ "maxTokenCount", 1024 },
{ "temperature", 1f },
{ "topP", 0.6f }
}
}
}.ToJsonString();

string generatedText = "";
try
{
InvokeModelResponse response = await client.InvokeModelAsync(new InvokeModelRequest()
{
ModelId = titanTextG1ModelId,
Body = AWSSDKUtils.GenerateMemoryStreamFromString(payload),
ContentType = "application/json",
Accept = "application/json"
});

if (response.HttpStatusCode == System.Net.HttpStatusCode.OK)
{
var results = JsonNode.ParseAsync(response.Body).Result?["results"]?.AsArray();

return results is null ? "" : string.Join(" ", results.Select(x => x?["outputText"]?.GetValue<string?>()));
}
else
{
logger.LogError("InvokeModelAsync failed with status code " + response.HttpStatusCode);
}
}
catch (AmazonBedrockRuntimeException e)
{
logger.LogError(e.Message);
}
return generatedText;
}
}
GraderController
When a student defeats a monster, this function is activated. It executes the corresponding NUnit tests in the ProjectTestsLib for a specific task. The function then stores the raw test result XML, auto-converted JSON, and test output log in the Test Result S3 bucket. A task may consist of multiple tests. Passed tests are saved to the Passed Test Table, while failed tests are recorded in the Failed Test Table. This design allows for tracking student progress and trial behavior. Additionally, students can retrieve the most recent failed test output log from the mark’s web page by S3 presigned URL.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
private void RunTestProcess(AwsTestConfig awsTestConfig, string tempDir, string tempCredentialsFilePath, out StringWriter strWriter, out int returnCode)
{
var filter = GetFilter(awsTestConfig);
strWriter = new StringWriter();
var autoRun = new AutoRun(typeof(Constants).GetTypeInfo().Assembly);
var runTestParameters = new List<string>
{
"/test:"+nameof(ProjectTestsLib),
"--work=" + tempDir,
"--output=" + tempDir,
"--err=" + tempDir,
"--params:AwsTestConfig=" + tempCredentialsFilePath + ";trace=" + awsTestConfig.Trace
};
runTestParameters.Insert(1, "--where=" + filter);
logger.LogInformation(string.Join(" ", runTestParameters));
returnCode = autoRun.Execute([.. runTestParameters], new ExtendedTextWrapper(strWriter), Console.In);
logger.LogInformation("returnCode:" + returnCode);
}
KeyGenController
It provides simple URL to generate email hash for each student, and we can use Google Spreadsheet to create key easily.
1
=IMPORTDATA("https://xxxx.execute-api.us-east-1.amazonaws.com/api/keygen?email="&I117&"&hash=abcdefg")

Then, we use mail merge to send the hash key to students.
GitHub Repo
Not all unit tests have been included, as my students are still working on this assignment project.

Deployment

1. Fork the repo.
2. Create a new Codesplaces.
3. Set configure AWS CLI.
4. Run “./deploy.sh b14ca5898a4e4133bbce2e123456123456” and remember to change the hash.

Further Development

Students can design their own characters and monsters using the Amazon Titan Image Generator. Additionally, they can replace the hash key logic with an Amazon API Gateway usage plan that utilizes an API key. Furthermore, it is recommended to include NUnit tests in all free AWS Educate lab exercises.

Conclusion

"The Legend of AWS Warrior" is an innovative approach to learning AWS through gaming at HKIIT. By integrating various AWS services such as AWS Lambda, Amazon S3, and Amazon DynamoDB into an AWS SAM serverless application, it offers a practical, hands-on experience. It's designed to help students using the AWS Academy Learner Lab and AWS Educate Lab to engage with cloud computing concepts in a fun and interactive way through Amazon Bedrock. The game allows for customization, enabling educators and learners to add new unit tests and expand the learning framework. This project not only makes learning about AWS more accessible but also encourages the development of practical skills in a simulated environment.

About the Author

Cyrus Wong is the senior lecturer of [Hong Kong Institute of Information Technology (HKIIT) @ IVE(Lee Wai Lee).](http://lwit.vtc.edu.hk/aboutmit_message.html) and he focuses on teaching public Cloud technologies. He is a passionate advocate for the adoption of cloud technology across various media and events. With his extensive knowledge and expertise, he has earned prestigious recognitions such as AWS Machine Learning Hero, Microsoft Azure MVP, and Google Developer Expert for Google Cloud Platform.

 

Comments