How to integrate computing time in AI reaction? - lua

My program is an implementation of Pong where one of the paddles is to be moved by the computer and the other by the user.
The program works fine with errors being made by the AI for realism. However, the movement of my paddle on screen is stuttering and it seems to be skipping one or two frames.
The program is in Lua(+Love2D).
function Paddle:comp_move(dt)
error = math.random(3) == 2 and true or false
start_time = os.time()
diff = 0
if ball:collides(self) == false then
if ball.y > self.y + self.height then -- Ball is below paddle
-- Ball is moving up and difference is more than 20 pixels
if ball.dy < 0 and (ball.y - self.y - self.height) > 20 then
-- move down
if error == false then
diff = os.difftime(os.time() - start_time)
self.y = math.min(VIRTUAL_HEIGHT - 20, self.y + PADDLE_SPEED*(dt))
end
end
-- Ball is moving down
if ball.dy > 0 then
-- move down
if error == false then
diff = os.difftime(os.time() - start_time)
self.y = math.min(VIRTUAL_HEIGHT - 20, self.y + PADDLE_SPEED*(dt))
end
end
elseif ball.y + ball.height < self.y then -- Ball is above paddle
-- Ball is moving down
if ball.dy > 0 and (self.y - ball.y - ball.height) > 20 then
-- move up
if error == false then
diff = os.difftime(os.time() - start_time)
self.y = math.max(0,self.y - PADDLE_SPEED*(dt))
end
elseif ball.dy < 0 then -- Ball is moving up
-- move up
if error == false then
diff = os.difftime(os.time() - start_time)
self.y = math.max(0,self.y - PADDLE_SPEED*(dt))
end
end
end
end
I am calculating the time taken to compute by the PC but what operation should i do with it normalize my paddle's movement.

From your code it seems the paddle speed is constant; it might be an idea to replace this by a function, so that initially the paddle moves more slowly, then speeds up, and then slows down again as it arrives at the desired location.
This would make it more realistic in terms of movement behaviour, and it might also solve your problem of stuttering.
Your constant PADDLE_SPEED would need to be replaced by some function that takes a step and returns a value (probably between 0.0 and PADDLE_SPEED) at the appropriate time.

Related

Collision Detection in love2d(lua)

I am doing a game development course by cs50 where Colton Ogden teaches love2d which is in lua. I am facing a problem in collision detection. The below code/logic works fine when the object is not rotating example love.graphics.draw(texture, x, y, r).
Here x and y is some value and r(rotation is 0). Value of x and y keeps on changing.
function Projectile:collides(target)
if self.x > target.x + target.width or target.x > self.x + self.width then
return false
end
if self.y > target.y + target.height or target.y > self.y + self.height then
return false
end
return true
end
But the above code doesn't work for target which is rotating i.e., r has some value which keeps on changing.
Below is how i am rotating an object.
function Meteor:init(speed)
self.r = math.random(-1, 1)
end
function Meteor:update(dt)
if self.r > 0 then
self.r = self.r + math.pi/9 * dt
elseif self.r < 0 then
self.r = self.r - math.pi/9 * dt
else
self.r = self.r
end
if self.y < VIRTUAL_HEIGHT + 110 then
self.y = self.y + self.speed * dt
else
self.remove = true
end
end
function Meteor:render()
love.graphics.drawLayer(self.meteor, self.type, self.x, self.y, self.r)
end
Sometimes it is easier and sufficient to just approximate an object's collision. In LÖVE you can specify the texture offset in love.graphics.draw(). Let the texture rotate at its center and see if an approximation works.
If you really need rectangles, which are not axis aligned, you may want to use a collision library. HardonCollider works perfect.
If you plan on adding physics too, then the LÖVE physics module makes more sense.
As #Luke100000 wrote LÖVE has already a collision detection in love.physics implemented.
Read and try: https://love2d.org/wiki/Tutorial:PhysicsCollisionCallbacks
For a zero gravity world i use for example...
function love.load()
local meter=4.5
love.physics.setMeter(meter)
ZeroGravity=love.physics.newWorld(0,0,true)
ZeroGravity:setCallbacks(Hit,Release,preSolve,postSolve) -- <Collision Callbacks>
end
And for example the "Hit" callback function...
-- <Collision Callback Functions>
Hit=function(a,b)
a:getBody():setUserData(os.date('%H:%M:%S'))
b:getBody():setUserData(os.date('%H:%M:%S'))
a:getUserData():emit(256)
b:getUserData():emit(256)
return true
end
The particlesystem that emitting 256 parts at "Hit" is assigned to the fixture in userdata of an physics body at creation...
function addAsteroid(x,y,r,kind)
local body=love.physics.newBody(ZeroGravity,x,y,kind)
local shape=love.physics.newCircleShape(r)
local fixture=love.physics.newFixture(body,shape,.01)
fixture:setUserData(psystem:clone()) -- Particlesystem for emitting
fixture:setDensity(1)
fixture:setFriction(1)
fixture:setRestitution(1)
--fixture:setFilterData(1,1,-1)
fixture:getUserData():start()
return body
end
A particle system can be very complex but it is worth for learning it.
Here i give you an example of mine that works with quads...
-- planets.lua
fields=1
quads={love.graphics.newImage("planets.png")
}
local assets={x=0,y=0,size=.1,time=-1,
[1]=love.graphics.newQuad(138,33,300,300,quads[1]:getDimensions()),
[2]=love.graphics.newQuad(505,37,300,300,quads[1]:getDimensions()),
[3]=love.graphics.newQuad(871,21,300,300,quads[1]:getDimensions()),
[4]=love.graphics.newQuad(1238,22,300,300,quads[1]:getDimensions()),
[5]=love.graphics.newQuad(1606,15,300,300,quads[1]:getDimensions()),
[6]=love.graphics.newQuad(1974,11,300,300,quads[1]:getDimensions()),
[7]=love.graphics.newQuad(1976,349,300,300,quads[1]:getDimensions()),
[8]=love.graphics.newQuad(136,382,300,300,quads[1]:getDimensions()),
[9]=love.graphics.newQuad(504,379,300,300,quads[1]:getDimensions()),
[10]=love.graphics.newQuad(871,366,300,300,quads[1]:getDimensions()),
[11]=love.graphics.newQuad(1236,362,300,300,quads[1]:getDimensions()),
[12]=love.graphics.newQuad(19,724,300,300,quads[1]:getDimensions()),
[13]=love.graphics.newQuad(1047,698,300,300,quads[1]:getDimensions()),
[14]=love.graphics.newQuad(1376,687,300,300,quads[1]:getDimensions()),
[15]=love.graphics.newQuad(1721,686,300,300,quads[1]:getDimensions()),
[16]=love.graphics.newQuad(2042,684,300,300,quads[1]:getDimensions()),
[17]=love.graphics.newQuad(347,704,660,300,quads[1]:getDimensions())
}
pquads={x=0,y=0,size=1,time=-1}
for i=12,12 do table.insert(pquads,assets[i]) end -- recall
assets=empty
psystem=love.graphics.newParticleSystem(quads[1],72)
psystem:setBufferSize(4096)
psystem:setLinearAcceleration(-150,-150,150,150) -- Random movement in all directions.
psystem:setEmissionArea('ellipse',145,145,math.rad(math.random(360)),false)
psystem:setQuads(pquads)
psystem:setSpread(75)
--[[psystem:setColors({love.math.random(),
love.math.random(),
love.math.random(),
0.3},{love.math.random(),
love.math.random(),
love.math.random(),
0.6},{love.math.random(),
love.math.random(),
love.math.random(),
0.3})]]--
--psystem:setColors({1,0,0,1},{0,0,1,1},{1,0,0,1})
--psystem:setColors({0,0,1,1},{1,1,1,1},{1,1,0,1},{1,0,0,1})
psystem:setSizes(math.random()*.05,0)
--psystem:setSizes(.1,.2,.3,.4,.3,.2,.1)
psystem:setSizeVariation(0)
psystem:setEmitterLifetime(pquads.time)
psystem:setParticleLifetime(pquads.size*11)
psystem:setEmissionRate(pquads.size*.11)
psystem:setInsertMode('top')
-- psystem:setPosition(-150,-150)
The assets i am using i get from...
https://opengameart.org/art-search?keys=planets

I am trying to create an object AllBalls in my Playstate but when ever i try to initialize the object i get an error

In my playstatue I create an instance if my object as such
function PlayState:enter(params)
self.paddle = params.paddle
self.bricks = params.bricks
self.health = params.health
self.score = params.score
self.highScores = params.highScores
self.level = params.level
self.recoverPoints = 5000
self.extendPoints = 7000
-- give ball random starting velocity
self.inPlayBalls= AllBalls(params.ball)
self.inPlayBalls[1].dx = math.random(-200, 200)
self.inPlayBalls[1].dy = math.random(-50, -60)
end
the AllBalls class is a simple class which goes as such
AllBalls = Class{}
function AllBalls:init(p)
self.ball=p
self.inPlayBalls={self.ball}
end
function AllBalls:update(dt,target)
for k, ball in pairs(self.inPlayBalls) do
ball:update(dt)
if ball:collides(target) then
-- raise ball above paddle in case it goes below it, then reverse dy
ball.y = target.y - 8
ball.dy = -target.dy
--
-- tweak angle of bounce based on where it hits the paddle
--
-- if we hit the paddle on its left side while moving left...
if ball.x < target.x + (target.width / 2) and target.dx < 0 then
ball.dx = -50 + -(8 * (target.x + target.width / 2 - ball.x))
-- else if we hit the paddle on its right side while moving right...
elseif ball.x > target.x + (target.width / 2) and target.dx > 0 then
ball.dx = 50 + (8 * math.abs(target.x + target.width / 2 - ball.x))
end
gSounds['paddle-hit']:play()
end
if math.abs(ball.dy) < 150 then
ball.dy = ball.dy * 1.02
end
if ball.remove then
table.remove(self.AllBalls, k)
end
end
end
function AllBalls:collides(target)
for k, ball in pairs(self.inPlayBalls) do
if ball:collides(target) then
if ball.x + 2 < brick.x and ball.dx > 0 then
-- flip x velocity and reset position outside of brick
ball.dx = -ball.dx
ball.x = brick.x - 8
-- right edge; only check if we're moving left, , and offset the check by a couple of pixels
-- so that flush corner hits register as Y flips, not X flips
elseif ball.x + 6 > brick.x + brick.width and ball.dx < 0 then
-- flip x velocity and reset position outside of brick
ball.dx = -ball.dx
ball.x = brick.x + 32
-- top edge if no X collisions, always check
elseif ball.y < brick.y then
-- flip y velocity and reset position outside of brick
ball.dy = -ball.dy
ball.y = brick.y - 8
-- bottom edge if no X collisions or top collision, last possibility
else
-- flip y velocity and reset position outside of brick
ball.dy = -ball.dy
ball.y = brick.y + 16
end
end
end
end
function AllBalls:add(ball)
local newBall=Ball(ball.skin)
newBall.x=ball.x
newBall.dx=math.random(-200,200)
newBall.y=ball.y
newBall.dy=ball.dy
table.insert(self.inPlayBalls, newBall)
end
function AllBalls:out()
for k, ball in pairs(self.inPlayBalls) do
if ball.y >= VIRTUAL_HEIGHT then
ball.remove=true
end
end
end
when ever i enter the playstate I get the following error
src/states/PlayStatue.lua:37 attempt to index nil value
I am new to Lua as a whole and i probably made a silly mistake that i couldn't find so any help would be appricated
The problem likely comes from this assignment
self.inPlayBalls= AllBalls(params.ball)
From looking at your GitHub, inPlayBalls is an attribute of an AllBalls instance. That means you would have to create your AllBalls instance first, then get the attribute to assign it to a variable, such as in the following:
self.allBalls = AllBalls(params.ball)
self.inPlayBalls = self.allBalls.inPlayBalls

How to make love2d game compatible

I am getting the message "This game indicates it was made for version '0.9.1' of LOVE.
It may not be compatible with the running version (0.10.2)." when I try and run my game. The game still works but the message is annoying me. How do I update it to the latest version? My code is here:
debug = true
Main.lua
-- Timers
-- We declare these here so we don't have to edit them multiple places
canShoot = true
canShootTimerMax = 0.2
canShootTimer = canShootTimerMax
createEnemyTimerMax = 0.4
createEnemyTimer = createEnemyTimerMax
-- Player Object
player = { x = 200, y = 710, speed = 150, img = nil }
isAlive = true
score = 0
-- Image Storage
bulletImg = nil
enemyImg = nil
-- Entity Storage
bullets = {} -- array of current bullets being drawn and updated
enemies = {} -- array of current enemies on screen
-- Collision detection taken function from http://love2d.org/wiki/BoundingBox.lua
-- Returns true if two boxes overlap, false if they don't
-- x1,y1 are the left-top coords of the first box, while w1,h1 are its width and height
-- x2,y2,w2 & h2 are the same, but for the second box
function CheckCollision(x1,y1,w1,h1, x2,y2,w2,h2)
return x1 < x2+w2 and
x2 < x1+w1 and
y1 < y2+h2 and
y2 < y1+h1
end
-- Loading
function love.load(arg)
player.img = love.graphics.newImage('assets/plane.png')
enemyImg = love.graphics.newImage('assets/enemy.png')
bulletImg = love.graphics.newImage('assets/bullet.png')
end
-- Updating
function love.update(dt)
-- I always start with an easy way to exit the game
if love.keyboard.isDown('escape') then
love.event.push('quit')
end
-- Time out how far apart our shots can be.
canShootTimer = canShootTimer - (1 * dt)
if canShootTimer < 0 then
canShoot = true
end
-- Time out enemy creation
createEnemyTimer = createEnemyTimer - (1 * dt)
if createEnemyTimer < 0 then
createEnemyTimer = createEnemyTimerMax
-- Create an enemy
randomNumber = math.random(10, love.graphics.getWidth() - 10)
newEnemy = { x = randomNumber, y = -10, img = enemyImg }
table.insert(enemies, newEnemy)
end
-- update the positions of bullets
for i, bullet in ipairs(bullets) do
bullet.y = bullet.y - (250 * dt)
if bullet.y < 0 then -- remove bullets when they pass off the screen
table.remove(bullets, i)
end
end
-- update the positions of enemies
for i, enemy in ipairs(enemies) do
enemy.y = enemy.y + (200 * dt)
if enemy.y > 850 then -- remove enemies when they pass off the screen
table.remove(enemies, i)
end
end
-- run our collision detection
-- Since there will be fewer enemies on screen than bullets we'll loop them first
-- Also, we need to see if the enemies hit our player
for i, enemy in ipairs(enemies) do
for j, bullet in ipairs(bullets) do
if CheckCollision(enemy.x, enemy.y, enemy.img:getWidth(), enemy.img:getHeight(), bullet.x, bullet.y, bullet.img:getWidth(), bullet.img:getHeight()) then
table.remove(bullets, j)
table.remove(enemies, i)
score = score + 1
end
end
if CheckCollision(enemy.x, enemy.y, enemy.img:getWidth(), enemy.img:getHeight(), player.x, player.y, player.img:getWidth(), player.img:getHeight())
and isAlive then
table.remove(enemies, i)
isAlive = false
end
end
if love.keyboard.isDown('left','a') then
if player.x > 0 then -- binds us to the map
player.x = player.x - (player.speed*dt)
end
elseif love.keyboard.isDown('right','d') then
if player.x < (love.graphics.getWidth() - player.img:getWidth()) then
player.x = player.x + (player.speed*dt)
end
end
if love.keyboard.isDown(' ', 'rctrl', 'lctrl', 'ctrl') and canShoot then
-- Create some bullets
newBullet = { x = player.x + (player.img:getWidth()/2), y = player.y, img = bulletImg }
table.insert(bullets, newBullet)
canShoot = false
canShootTimer = canShootTimerMax
end
if not isAlive and love.keyboard.isDown('r') then
-- remove all our bullets and enemies from screen
bullets = {}
enemies = {}
-- reset timers
canShootTimer = canShootTimerMax
createEnemyTimer = createEnemyTimerMax
-- move player back to default position
player.x = 50
player.y = 710
-- reset our game state
score = 0
isAlive = true
end
end
-- Drawing
function love.draw(dt)
for i, bullet in ipairs(bullets) do
love.graphics.draw(bullet.img, bullet.x, bullet.y)
end
for i, enemy in ipairs(enemies) do
love.graphics.draw(enemy.img, enemy.x, enemy.y)
end
love.graphics.setColor(255, 255, 255)
love.graphics.print("SCORE: " .. tostring(score), 400, 10)
if isAlive then
love.graphics.draw(player.img, player.x, player.y)
else
love.graphics.print("Press 'R' to restart", love.graphics:getWidth()/2-50, love.graphics:getHeight()/2-10)
end
if debug then
fps = tostring(love.timer.getFPS())
love.graphics.print("Current FPS: "..fps, 9, 10)
end
end
conf.lua
-- Configuration
function love.conf(t)
t.title = "Scrolling Shooter Tutorial" -- The title of the window the game is in (string)
t.version = "0.9.1" -- The LÖVE version this game was made for (string)
t.window.width = 480 -- we want our game to be long and thin.
t.window.height = 800
-- For Windows debugging
t.console = true
end
As noted in the comments, the problem in your case was that, in conf.lua, the version was specified as "0.9.1". In some cases, changing this value to "0.10.2" is sufficient, but a significant amount of changes occurred between 0.9.0 and 0.10.0.
Be especially aware that mouse input is definitely going to be broken because, in versions before 0.10.0, LOVE used strings to represent mouse buttons, whereas in 0.10.0 and beyond, numbers are used. To fix this, look for mouse-related functions (love.mouse.isDown, love.mousepressed, etc.) and change "l" to 1, "r" to 2, and so on. See the full list of old values and love.mousepressed for more. Additionally, mousewheel movement changed as well, with the addition of the love.wheelmoved callback and removing the strings passed to love.mousepressed.
Additionally, read through the changelog for any changes that may have affected your program.

How to constantly check the location of an object in lua

I am working on a game that involves drawing lines to guide a physics ball into a bucket at the bottom of the screen. However, sometimes the user might draw the lines poorly, and the balls will become stuck. To rid this I would like to check the position of the balls every 3 seconds.
This is what I thought would work:
function checkBallVelocity(event)
startX = event.x
startY = event.y
timer.performWithDelay( 5000, function()
endX = event.x
endY = event.y
print(startX..","..startY.." || "..endX..","..endY)
if startX == endX or startY == endY then
if event.other.name == "1" then
circle1:removeSelf( )
circle1 = nil
ballsMissed = ballsMissed + 1
elseif event.other.name == "2" then
circle2:removeSelf( )
circle2 = nil
ballsMissed = ballsMissed + 1
elseif event.other.name == "3" then
circle3:removeSelf( )
circle3 = nil
ballsMissed = ballsMissed + 1
end
return 1
else
checkBallVelocity()
end
end
)
end
Sadly, it doesn't. Any help/advice would be welcome
There are many ways to do this sort of thing, but Corona provides an enterFrame event which fires each time a frame is drawn (30 or 60 frames per second). It can be used as a main loop to perform periodic actions, but keep in mind that it is run quite often so it helps to define a period and limit your actions to certain phases within the period.
local period
local phase = 0
function mainLoop(event)
phase = phase + 1
if phase == 1 then
checkBallPosition(...)
elseif phase == period
phase = 0
end
end
...
function scene:enterScene( event )
period = display.fps * 3 -- # of seconds
Runtime:addEventListener("enterFrame", mainLoop)
...
end

Corona Lua display object y property out by three ten-millionths of a pixel (!)

When I run this code (which is based on the "DragMe" sample app) I get very unexpected results in very particular circumstances.
My "snapToGrid" function sets x and y to the nearest round 100 value after a drag.
The "examine" function outputs the x and y values after an object is created, moved or rotated (by clicking on it).
If you place an object in row 5 (so y = 500) and rotate it you will see that the y value changes to 499.99996948242 but this does not happen in any other row.
How can this be explained? I notice this does not happen if the physics bodies are not added to the display object.
I know I could call snapToGrid after rotation or round the value before I use it for anything else but I think there is an important opportunity here for me to learn something useful about Corona.
local function snapToGrid(t)
modHalf = t.x % t.width
if modHalf > t.width/2 then
t.x = t.x + (t.width-modHalf)
end
if modHalf < t.width/2 then
t.x = t.x - modHalf
end
modHalfY = t.y % t.height
if modHalfY > t.height/2 then
t.y = t.y + (t.height-modHalfY)
end
if modHalfY < t.height/2 then
t.y = t.y - modHalfY
end
display.getCurrentStage():setFocus( nil )
t.isFocus = false
return true
end
function rotatePiece(target)
if target.rotation == 270 then
target.rotation = 0
else
target.rotation = target.rotation + 90
end
end
local function dragBody(event)
local target = event.target
local phase = event.phase
local halfWidth = target.width/2
local halfHeight = target.height/2
--get tileX and tileY relative to tile centre
local tileX = event.x-target.x
local tileY = event.y-target.y
local modHalf = ""
local snap = 15
if phase == "began" then
-- Make target the top-most object
display.getCurrentStage():setFocus( target )
-- Spurious events can be sent to the target, e.g. the user presses
-- elsewhere on the screen and then moves the finger over the target.
-- To prevent this, we add this flag. Only when it's true will "move"
-- events be sent to the target.
target.isFocus = true
-- Store initial position
target.x0 = event.x - target.x
target.y0 = event.y - target.y
elseif target.isFocus then
if phase == "moved" then
-- Make object move (we subtract target.x0,target.y0 so that moves are
-- relative to initial grab point, rather than object "snapping").
target.x = event.x - target.x0
target.y = event.y - target.y0
if target.x > display.contentWidth - (target.width/2) then target.x = display.contentWidth - (target.width/2) end
if target.y > display.contentHeight - (target.height/2) then target.y = display.contentHeight - (target.height/2) end
if target.x < 0 + (target.width/2) then target.x = 0 + (target.width/2) end
if target.y < 0 + (target.height/2) then target.y = 0 + (target.height/2) end
modHalf = target.x % target.width
if modHalf < snap then
target.x = target.x - modHalf
end
if modHalf > ((target.width) - snap) then
target.x = target.x + ((target.width)-modHalf)
end
modHalfY = target.y % target.height
if modHalfY < snap then
target.y = target.y - modHalfY
end
if modHalfY > ((target.height) - snap) then
target.y = target.y + ((target.height)-modHalfY)
end
hasMoved = true
return true
elseif phase == "ended" then
if hasMoved then
hasMoved = false
snapToGrid(target)
--tile has moved
examine(target)
return true
else
--rotate piece
rotatePiece(target)
display.getCurrentStage():setFocus( nil )
target.isFocus = false
--tile has rotated
examine(target)
return true
end
end
end
-- Important to return true. This tells the system that the event
-- should not be propagated to listeners of any objects underneath.
return true
end
local onTouch = function(event)
if event.phase == "began" then
local tile = {}
img = display.newRect(event.x,event.y,100,100)
img:addEventListener( "touch", dragBody )
snapToGrid(img)
--top right corner and top middle solid
topRight = {16,-16,16,50,50,50,50,-16}
--top left and left middle solid
topLeft = {-16,-16,-16,-50,50,-50,50,-16}
--bottom right and right middle solid
bottomRight = {16,16,16,50,-50,50,-50,16}
--bottom left and bottom middle solid
bottomLeft = {-16,16,-16,-50,-50,-50,-50,16}
physics.addBody( img, "static",
{shape=topRight},
{shape=topLeft},
{shape=bottomLeft},
{shape=bottomRight}
)
--new tile created
examine(img)
return true
end
end
function examine(img)
print("--------------------------------------------------")
if img ~= nil then
print("X: "..img.x..", Y: "..img.y)
end
print("--------------------------------------------------")
end
local img
local physics = require( "physics" )
physics.setDrawMode( "hybrid" )
--draw gridlines
for i = 49, display.contentHeight, 100 do
local line = display.newLine(0,i,display.contentWidth,i)
local line2 = display.newLine(0,i+2,display.contentWidth,i+2)
end
for i = 49, display.contentWidth, 100 do
local line = display.newLine(i,0,i,display.contentHeight )
local line2 = display.newLine(i+2,0,i+2,display.contentHeight )
end
--init
physics.start()
Runtime:addEventListener("touch", onTouch)
There several possibilities. Firstly, since there are no integers in Lua, all numbers are double floating point values. According to FloatingPoint on Lua wiki,
Some vendors' printf implementations may not be able to handle
printing floating point numbers accurately. Believe it or not, some
may incorrectly print integers (that are floating point numbers). This
can manifest itself as incorrectly printing some numbers in Lua.
Indeed, try the following:
for i=1,50,0.01 do print(i) end
You will see that a lot of numbers print exactly as you would expect, by many print with an error of 10^-12 or even 2 x 10^-12.
However, your x prints fine when you don't give it to physics module. So surely the physics module does some computation on object position and changes it. I would certainly expect that for dynamic objects (due to collision detection), but here your object seems to be "static". So it must be that physics adjusts x even for static objects. The adjustment is so small that it would not be visible on the screen (you can't perceive any motion less than a pixel), but you're right that it is interesting to ask why.
The SO post Lua: subtracting decimal numbers doesn't return correct precision is worth reading; it has some links you might find interesting and gives the neat example that decimal 0.01 cannot be represented exactly in base 2; just like 1/3 can't be represented exactly in base 10 (but it can in base 3: it would be 0.1 base 3!). The question shows that, although Lua (or possibly, the underlying C) is smart enough to print 0.01 as 0.01, it fails to print 10.08-10.07 as 0.01.
If you really want to shake your understanding of floating point values, try this:
> a=0.3
> b=0.3
> print(a==b)
true
> -- so far so good; now this:
> a=0.15 + 0.15
> b=0.1 + 0.2
> print(a,b)
0.3 0.3
> print(c==d)
false -- woa!
This is explained by the fact that 0.15 in binary has a small error which is different from that of 0.1 or 0.2, so in terms of bits they are not identical; although when printed, the difference is too small to show. You may want to read the Floating Point Guide.
Actually this problem only occurs if you add the physics, so I think it is fair to say that the issue is caused by transferring control to box2d, not by Corona handling of the number alone. I asked on Corona forums and got this answer.
http://forums.coronalabs.com/topic/46245-display-object-that-has-static-physics-body-moves-very-slightly-when-rotated/

Resources